HaProxy源码分析1:编译&gdb&1.0.0配置文件解析

本来打算从官网下载,无法访问,感谢党和政府,心中无数个草泥马。

然后取道github: https://github.com/haproxy/haproxy/

这里最old的版本是:1.0.0.所以这里先从1.0.0开始分析。

---------先说下编译环境:

ubuntu 12.04.5

wget https://github.com/haproxy/haproxy/archive/v1.0.0.tar.gz

tar -zvxf v1.0.0.tar.gz

cd haproxy-1.0.0/

make

然后执行gdb,没有问题

HaProxy源码分析1:编译&gdb&1.0.0配置文件解析_第1张图片

 

为了在windows里跟踪代码, 通过SourceInsight3.5打开HaProxy1.0.0一起分析。

下面正式分析源码:

gdb ./haproxy
break main
run -D -f ./examples/cfg


 跟踪到配置文件解析模块,我们就来看看文件解析函数

/*
 * This function reads and parses the configuration file given in the argument.
 * returns 0 if OK, -1 if error.
 */
int readcfgfile(char *file)


 if ((f=fopen(file,"r")) == NULL)
while (fgets(line = thisline, sizeof(thisline), f) != NULL) {
	linenum++;
可见是打开文件,然后读取一行一行进行分析。

下面是对配置文件的分析:

listen proxy1 0.0.0.0:3128



HaProxy会对这一行进行解析

结果如下:

(gdb) p args[0]
$43 = 0xbffff57c "listen"
(gdb) p args[1]
$44 = 0xbffff583 "proxy1"
(gdb) p args[2]
$45 = 0xbffff58a "0.0.0.0:3128"
(gdb) p args[3]
$46 = 0xbffff596 ""



也就是会把各个字段提取出来!

if (!strcmp(args[0], "listen")) {  /* new proxy */

	    if (strchr(args[2], ':') == NULL) 
		{
		Alert("parsing [%s:%d] : <listen> expects <id> and <addr:port> as arguments.\n",
		      file, linenum);
		return -1;
	    }



表明:如果第一个字段是listen的话,就是一个新的proxy,并且args[2】要包含":"

否则配置有问题!

----------------接下来就应该为这个proxy分配一个空间存放它的配置,所以有这么一段代码:

if ((curproxy = (struct proxy *)calloc(1, sizeof(struct proxy)))
		== NULL) 
		{
		Alert("parsing [%s:%d] : out of memory\n", file, linenum);
		exit(1);
	    }



然后要把当前proxy的信息挂到全局proxy链表里,所以有:

 curproxy->next = proxy;
 proxy = curproxy;



上面2行代码需要结合全局的一行代码来理解:

struct proxy *proxy  = NULL;	/* list of all existing proxies */



这个就不细说,学过C语言的都知道!

==============

然后是对struct proxy的若干变量的赋值,赋值结果暂不说。

第二行是:

mode	http



解析结果如下:

(gdb) p args[0]
$61 = 0xbffff57d "mode"
(gdb) p args[1]
$62 = 0xbffff582 "http"
(gdb) p args[2]
$63 = 0xbffff586 ""

这里对应的代码是:


if (!strcmp(args[0], "mode")) {  /* sets the proxy mode */
	    if (!strcmp(args[1], "http")) curproxy->mode = PR_MODE_HTTP;
		//检查:1 2 3
	    else if (!strcmp(args[1], "tcp")) curproxy->mode = PR_MODE_TCP;
	    else if (!strcmp(args[1], "health")) curproxy->mode = PR_MODE_HEALTH;
	    else {
		Alert("parsing [%s:%d] : unknown proxy mode <%s>.\n", file, linenum, args[1]);
		return -1;
	    }
	}

---接下来的一行是:

 cookie SERVERID



对应的代码:

else if (!strcmp(args[0], "cookie")) 
	{  /* cookie name */
	    if (curproxy->cookie_name != NULL) 
		{
		Alert("parsing [%s:%d] : cookie name already specified. Continuing.\n",
		      file, linenum);
		continue;
	    }

	    if (*(args[1]) == 0) {
		Alert("parsing [%s:%d] : <cookie> expects <cookie_name> as argument.\n",
		      file, linenum);
		return -1;
	    }
	    curproxy->cookie_name = strdup(args[1]);
	}



接下来的是:

server srv1 192.168.12.2:8080
server srv2 192.168.12.3:8080

解析结果就是:

 struct server *srv;			/* known servers */
	//可能有多个struct server *组成链表
	/*
	struct server 
	{
    	struct server *next;
    	
    	char *id;				/* the id found in the cookie *
    		//"srv1"
    	
    	struct sockaddr_in addr;		/* the address to connect to *
    		//192.168.12.2:8080
	};
	*/



---接下来的配置

contimeout 3000
 clitimeout 150000
 srvtimeout 150000

直接对应的结果就是:


int clitimeout;			/* client I/O timeout (in milliseconds) */
	//0->150000
	
    int srvtimeout;			/* server I/O timeout (in milliseconds) */
	// 0->150000
	
    int contimeout;			/* connect timeout (in milliseconds) */
	// 0->3000

maxconn 60000

int maxconn;			/* max # of active sessions */
	//->4000(cfg_maxpconn)->60000



---

接下来的是

redisp


int conn_redisp;			
/* allow to reconnect to dispatch in case of errors */
// 0->1

===

retries 3


curproxy->conn_retries = atol(args[1]);




    int conn_retries;			/* number of connect retries left */
	//CONN_RETRIES	3->3


最后一行是:grace 3000

curproxy->grace = atol(args[1]);


int grace;	/* grace time after stop request */
//->3000



这样,第一个proxy就解析完毕!

下面自然是解析第2个proxy

listen proxy2 0.0.0.0:3129
	mode	http
	dispatch 127.0.0.1:80
	contimeout	3000
	clitimeout	150000
	srvtimeout	150000
	maxconn 60000
	retries	3
	grace 3000

#	log	10.101.11.1 local1
#	log	10.101.11.1 local2

#	cliexp	^(.*ASPSESSIONID.*=)(.*) \1FENICGGCBECLFFEEOAEAIFGF
#	cliexp	^(GET.*)(.free.fr)(.*) \1.online.fr\3
#	cliexp	^(POST.*)(.free.fr)(.*) \1.online.fr\3
#	cliexp	^Proxy-Connection:.*	Proxy-Connection:\ close
#	srvexp	^(Location:\ )([^:]*://[^/]*)(.*) \1\3



未打#的几项配置,第一个proxy都存在,就不罗嗦了。

先看注释的:

#	log	10.101.11.1 local1
#	log	10.101.11.1 local2



命令解析结果为:

(gdb) p args[0]
$94 = 0xbffff57e "log"
(gdb) p args[1]
$95 = 0xbffff582 "10.101.11.1"
(gdb) p args[2]
$96 = 0xbffff58e "local1"



接下来是:

#	cliexp	^(.*ASPSESSIONID.*=)(.*) \1FENICGGCBECLFFEEOAEAIFGF
#	cliexp	^(GET.*)(.free.fr)(.*) \1.online.fr\3
#	cliexp	^(POST.*)(.free.fr)(.*) \1.online.fr\3
#	cliexp	^Proxy-Connection:.*	Proxy-Connection:\ close



其实是同一个命令:

源码如下:

else if (!strcmp(args[0], "cliexp")) 
	{  /* client regex */
	    regex_t *preg;
	    if (curproxy->nb_cliexp >= MAX_REGEXP) {
		Alert("parsing [%s:%d] : too many client expressions. Continuing.\n",
		      file, linenum);
		continue;
	    }

	    if (*(args[1]) == 0 || *(args[2]) == 0) {
		Alert("parsing [%s:%d] : <cliexp> expects <search> and <replace> as arguments.\n",
		      file, linenum);
		return -1;
	    }

	    preg = calloc(1, sizeof(regex_t));
	    if (regcomp(preg, args[1], REG_EXTENDED) != 0) {
		Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]);
		return -1;
	    }
	    curproxy->cli_exp[curproxy->nb_cliexp].preg = preg;
	    curproxy->cli_exp[curproxy->nb_cliexp].replace = strdup(args[2]);
	    curproxy->nb_cliexp++;
	}



结果就是:

struct hdr_exp cli_exp[MAX_REGEXP];	/* regular expressions for client headers */
	/*
	    regex_t *preg;	/* expression to look for *
	    	//第一列
    	char *replace;	/* expression to set instead *
    		//第二列
	*/
	int nb_cliexp;
	//总的个数



srvexp ^(Location:\ )([^:]*://[^/]*)(.*) \1\3

 也是一样的道理!

---

最后一项配置就是:

listen health 0.0.0.0:3130
	mode	health
	clitimeout	1500
	srvtimeout	1500
	maxconn 4
	grace 0



这里唯一的不同就是:

mode	health



if (!strcmp(args[0], "mode")) {  /* sets the proxy mode */
	    if (!strcmp(args[1], "http")) curproxy->mode = PR_MODE_HTTP;
		//检查:1 2 3
	    else if (!strcmp(args[1], "tcp")) curproxy->mode = PR_MODE_TCP;
	    else if (!strcmp(args[1], "health")) curproxy->mode = PR_MODE_HEALTH;
	    else {
		Alert("parsing [%s:%d] : unknown proxy mode <%s>.\n", file, linenum, args[1]);
		return -1;
	    }
	}



说明是IP层探测!

好,配置文件解析完毕!

看了半天,发现old版本使用的是selectAPI,后续将分析1.3.0 版本!





 


 



 

 


 


 

 


 

 

 

 

你可能感兴趣的:(haproxy)