现在的位置: 首页 > 综合 > 正文

haproxy工作流程分析

2017年09月09日 ⁄ 综合 ⁄ 共 6127字 ⁄ 字号 评论关闭

本文主要是分析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的主函数了,里面还有很多逻辑没有详细的分析。

抱歉!评论已关闭.