本文主要是分析haproxy的启动及工作流程,我们首先找到haproxy的主函数,一找好多main函数啊,这到底哪个才是haproxy工作的主函数呢?不用急,仔细的看看那些main函数,很多都是test里面的,这是用来测试的,haproxy工作的主函数在src/haproxy.c,下面我们来分析一下这里面的main函数,一起学习其工作流程及初始化过程:
int main(int agrc, char *agrv) { init(argc, argv); /* 运行所需的初始化 */ /* 注册安装各种信号 */ signal_register(SIGQUIT, dump); signal_register(SIGUSR1, sig_soft_stop); signal_register(SIGHUP, sig_dump_state); #ifdef DEBUG_MEMORY signal_register(SIGINT, sig_int); signal_register(SIGTERM, sig_term); #endif /* Always catch SIGPIPE even on platforms which define MSG_NOSIGNAL. * Some recent FreeBSD setups report broken pipes, and MSG_NOSIGNAL * was defined there, so let's stay on the safe side. */ signal(SIGPIPE, SIG_IGN); …… /* We will loop at most 100 times with 10 ms delay each time. * That's at most 1 second. We only send a signal to old pids * if we cannot grab at least one port. */ /* 创建侦听sock,若没有创建成功则循环200次,每次间隔10ms,创建成功则跳出 */ retry = MAX_START_RETRIES; err = ERR_NONE; while (retry >= 0) { struct timeval w; err = start_proxies(retry == 0 || nb_oldpids == 0); /* exit the loop on no error or fatal error */ if ((err & (ERR_RETRYABLE|ERR_FATAL)) != ERR_RETRYABLE) break; if (nb_oldpids == 0 || retry == 0) break; /* FIXME-20060514: Solaris and OpenBSD do not support shutdown() on * listening sockets. So on those platforms, it would be wiser to * simply send SIGUSR1, which will not be undoable. */ if (tell_old_pids(SIGTTOU) == 0) { /* no need to wait if we can't contact old pids */ retry = 0; continue; } /* give some time to old processes to stop listening */ w.tv_sec = 0; w.tv_usec = 10*1000; select(0, NULL, NULL, NULL, &w); retry--; } /* Note: start_proxies() sends an alert when it fails. */ if ((err & ~ERR_WARN) != ERR_NONE) { if (retry != MAX_START_RETRIES && nb_oldpids) { protocol_unbind_all(); /* cleanup everything we can */ tell_old_pids(SIGTTIN); } exit(1); } if (listeners == 0) { Alert("[%s.main()] No enabled listener found (check the <listen> keywords) ! Exiting.\n", argv[0]); /* Note: we don't have to send anything to the old pids because we * never stopped them. */ exit(1); } if ((protocol_bind_all() & ~ERR_WARN) != ERR_NONE) { Alert("[%s.main()] Some protocols failed to start their listeners! Exiting.\n", argv[0]); protocol_unbind_all(); /* cleanup everything we can */ if (nb_oldpids) tell_old_pids(SIGTTIN); exit(1); } /* prepare pause/play signals */ signal_register(SIGTTOU, sig_pause); signal_register(SIGTTIN, sig_listen); /* MODE_QUIET can inhibit alerts and warnings below this line */ global.mode &= ~MODE_STARTING; if ((global.mode & MODE_QUIET) && !(global.mode & MODE_VERBOSE)) { /* detach from the tty */ fclose(stdin); fclose(stdout); fclose(stderr); } /* open log & pid files before the chroot */ if (global.mode & MODE_DAEMON && global.pidfile != NULL) { int pidfd; unlink(global.pidfile); pidfd = open(global.pidfile, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (pidfd < 0) { Alert("[%s.main()] Cannot create pidfile %s\n", argv[0], global.pidfile); if (nb_oldpids) tell_old_pids(SIGTTIN); protocol_unbind_all(); exit(1); } pidfile = fdopen(pidfd, "w"); } #ifdef CONFIG_HAP_CTTPROXY if (global.last_checks & LSTCHK_CTTPROXY) { int ret; ret = check_cttproxy_version(); if (ret < 0) { Alert("[%s.main()] Cannot enable cttproxy.\n%s", argv[0], (ret == -1) ? " Incorrect module version.\n" : " Make sure you have enough permissions and that the module is loaded.\n"); protocol_unbind_all(); exit(1); } } #endif if ((global.last_checks & LSTCHK_NETADM) && global.uid) { Alert("[%s.main()] Some configuration options require full privileges, so global.uid cannot be changed.\n" "", argv[0]); protocol_unbind_all(); exit(1); } /* If the user is not root, we'll still let him try the configuration * but we inform him that unexpected behaviour may occur. */ if ((global.last_checks & LSTCHK_NETADM) && getuid()) Warning("[%s.main()] Some options which require full privileges" " might not work well.\n" "", argv[0]); /* chroot if needed */ if (global.chroot != NULL) { if (chroot(global.chroot) == -1) { Alert("[%s.main()] Cannot chroot(%s).\n", argv[0], global.chroot); if (nb_oldpids) tell_old_pids(SIGTTIN); protocol_unbind_all(); exit(1); } chdir("/"); } if (nb_oldpids) nb_oldpids = tell_old_pids(oldpids_sig); /* Note that any error at this stage will be fatal because we will not * be able to restart the old pids. */ /* setgid / setuid */ if (global.gid && setgid(global.gid) == -1) { Alert("[%s.main()] Cannot set gid %d.\n", argv[0], global.gid); protocol_unbind_all(); exit(1); } if (global.uid && setuid(global.uid) == -1) { Alert("[%s.main()] Cannot set uid %d.\n", argv[0], global.uid); protocol_unbind_all(); exit(1); } /* check ulimits */ limit.rlim_cur = limit.rlim_max = 0; getrlimit(RLIMIT_NOFILE, &limit); if (limit.rlim_cur < global.maxsock) { Warning("[%s.main()] FD limit (%d) too low for maxconn=%d/maxsock=%d. Please raise 'ulimit-n' to %d or more to avoid any trouble.\n", argv[0], (int)limit.rlim_cur, global.maxconn, global.maxsock, global.maxsock); } /* 若是用户配置了运行在后台运行模式,则新创建一个子进程来在后台运行 */ if (global.mode & MODE_DAEMON) { struct proxy *px; int ret = 0; int proc; /* the father launches the required number of processes */ for (proc = 0; proc < global.nbproc; proc++) { ret = fork(); if (ret < 0) { Alert("[%s.main()] Cannot fork.\n", argv[0]); protocol_unbind_all(); exit(1); /* there has been an error */ } else if (ret == 0) /* child breaks here */ break; if (pidfile != NULL) { fprintf(pidfile, "%d\n", ret); fflush(pidfile); } relative_pid++; /* each child will get a different one */ } /* close the pidfile both in children and father */ if (pidfile != NULL) fclose(pidfile); /* We won't ever use this anymore */ free(oldpids); oldpids = NULL; free(global.chroot); global.chroot = NULL; free(global.pidfile); global.pidfile = NULL; /* we might have to unbind some proxies from some processes */ px = proxy; while (px != NULL) { if (px->bind_proc && px->state != PR_STSTOPPED) { if (!(px->bind_proc & (1 << proc))) stop_proxy(px); } px = px->next; } if (proc == global.nbproc) exit(0); /* parent must leave */ /* if we're NOT in QUIET mode, we should now close the 3 first FDs to ensure * that we can detach from the TTY. We MUST NOT do it in other cases since * it would have already be done, and 0-2 would have been affected to listening * sockets */ if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) { /* detach from the tty */ fclose(stdin); fclose(stdout); fclose(stderr); global.mode &= ~MODE_VERBOSE; global.mode |= MODE_QUIET; /* ensure that we won't say anything from now */ } pid = getpid(); /* update child's pid */ setsid(); fork_poller(); } protocol_enable_all(); /* * That's it : the central polling loop. Run until we stop. */ run_poll_loop(); /* haproxy实现功能的函数 */ /* Free all Hash Keys and all Hash elements */ appsession_cleanup(); /* Do some cleanup */ deinit(); exit(0); }
这是haproxy的主函数,哇哦,好长啊,大家别被其吓到,我们接下来慢慢分析,haproxy是怎么样初始化的,怎么要工作的。
haproxy首先是调用init函数,该函数主要是各种信号、task及参数解析等各种初始化,这函数也很长,在这就不一一分析,等下篇博文进行分析,初始化完后信号注册、安装及各种limit的配置,接下来就是创建侦听sock了,最后就是运行run_poll_loop,这个函数就是haproxy处理业务逻辑的函数,这函数下篇一起分析,这边就不详细分析了,这就是haproxy的主函数了,里面还有很多逻辑没有详细的分析。