本来打算从官网下载,无法访问,感谢党和政府,心中无数个草泥马。
然后取道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,没有问题
为了在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 版本!