设为首页 收藏本站
查看: 2010|回复: 0

[经验分享] haproxy工作流程分析

[复制链接]

尚未签到

发表于 2015-11-20 10:37:14 | 显示全部楼层 |阅读模式
  本文主要是分析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[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(&quot;[%s.main()] Some protocols failed to start their listeners! Exiting.\n&quot;, 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(&quot;[%s.main()] Cannot create pidfile %s\n&quot;, argv[0], 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[0],
(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[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(&quot;[%s.main()] Some options which require full privileges&quot;
&quot; might not work well.\n&quot;
&quot;&quot;, argv[0]);
/* chroot if needed */
if (global.chroot != NULL) {
if (chroot(global.chroot) == -1) {
Alert(&quot;[%s.main()] Cannot chroot(%s).\n&quot;, argv[0], 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[0], 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[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(&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[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(&quot;[%s.main()] Cannot fork.\n&quot;, 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, &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、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-141455-1-1.html 上篇帖子: ubuntu10.04下haproxy+heartbeat配置高可用负载均衡(修改版|亲 下篇帖子: 用haproxy+redis实现分布式redis服务器
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表