LOCKLOSE 发表于 2015-11-20 10:37:14

haproxy工作流程分析

  本文主要是分析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(&quot;[%s.main()] No enabled listener found (check the <listen> keywords) ! Exiting.\n&quot;, argv);
/* 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(&quot;[%s.main()] Some protocols failed to start their listeners! Exiting.\n&quot;, argv);
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(&quot;[%s.main()] Cannot create pidfile %s\n&quot;, argv, global.pidfile);
if (nb_oldpids)
tell_old_pids(SIGTTIN);
protocol_unbind_all();
exit(1);
}
pidfile = fdopen(pidfd, &quot;w&quot;);
}
#ifdef CONFIG_HAP_CTTPROXY
if (global.last_checks & LSTCHK_CTTPROXY) {
int ret;
ret = check_cttproxy_version();
if (ret < 0) {
Alert(&quot;[%s.main()] Cannot enable cttproxy.\n%s&quot;,
argv,
(ret == -1) ? &quot;Incorrect module version.\n&quot;
: &quot;Make sure you have enough permissions and that the module is loaded.\n&quot;);
protocol_unbind_all();
exit(1);
}
}
#endif
if ((global.last_checks & LSTCHK_NETADM) && global.uid) {
Alert(&quot;[%s.main()] Some configuration options require full privileges, so global.uid cannot be changed.\n&quot;
&quot;&quot;, argv);
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(&quot;[%s.main()] Some options which require full privileges&quot;
&quot; might not work well.\n&quot;
&quot;&quot;, argv);
/* chroot if needed */
if (global.chroot != NULL) {
if (chroot(global.chroot) == -1) {
Alert(&quot;[%s.main()] Cannot chroot(%s).\n&quot;, argv, global.chroot);
if (nb_oldpids)
tell_old_pids(SIGTTIN);
protocol_unbind_all();
exit(1);
}
chdir(&quot;/&quot;);
}
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(&quot;[%s.main()] Cannot set gid %d.\n&quot;, argv, global.gid);
protocol_unbind_all();
exit(1);
}
if (global.uid && setuid(global.uid) == -1) {
Alert(&quot;[%s.main()] Cannot set uid %d.\n&quot;, argv, 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(&quot;[%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&quot;,
argv, (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(&quot;[%s.main()] Cannot fork.\n&quot;, argv);
protocol_unbind_all();
exit(1); /* there has been an error */
}
else if (ret == 0) /* child breaks here */
break;
if (pidfile != NULL) {
fprintf(pidfile, &quot;%d\n&quot;, 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的主函数了,里面还有很多逻辑没有详细的分析。
页: [1]
查看完整版本: haproxy工作流程分析