共计 3176 个字符,预计需要花费 8 分钟才能阅读完成。
目录
1 问题描述
2 问题分析
3 HAProxy 定位
3.1 connect(…)
3.2 tcpv4_connect_server 的指定
3.3 tcpv4_connect_server 的调用
3.4 connect_server 的调用
3.4 修改源码添加自定义日志
3.5 srv_dynamic_maxconn
4 解决方案
5 与 HAProxy 作者的邮件交流
5.1 发送邮件描述问题
5.2 对方回复
5.3 再次发送验证答案
5.4 对方的最终回应
6 后记
HAProxy 现网问题解决
1 问题描述
RMI 上线后,现网的接口总是报告异常。
2 问题分析
通过对 RMI 源码的理解,这个是在 RMI 客户端那边没有可用的连接时,需要创建一个新的连接,但是连接失败。
网络问题一般抓包可以定位,于是通过抓包发现失败的连接有个共同的现象,就是在 5 秒钟被 HAProxy 主动关闭,考虑到 HAProxy 的配置有个 connectTimeout 参数为 5 秒,应该是 HAProxy 连接后端的 RMI 服务器时失败。
通过抓包也验证了这一点,因为在 5 秒钟内,并没有搜到从 HAProxy 发起的对后端的 SYN 报文 (除了 check 导致的握手)。
于是大胆怀疑问题出在 HAProxy 这边,否则至少 HAProxy 应该发起主动连接才对。
此时猜测 HAProxy 没有拿到可用的服务器。
3 HAProxy 定位
3.1 connect(…)
刚开始怀疑是 HAProxy 没有拿到可用的服务器,那么从哪里入手解决问题呢?
考虑到如果 HAProxy 如果需要对远程服务器建立连接的话,肯定需要调用 connect(…) 这个 C 语言的 API, 所以全文搜索 connect(….)
在函数
中可以看到调用了 connect(…)
3.2 tcpv4_connect_server 的指定
查看 tcpv4_connect_server 的调用栈
上面这个代码是在 event_accept 函数中,也就是说在 session 中的 client 建立时,指定 session 的 server 端的 connect 函数,然后后面某个地方触发了 tcpv4_connect_server 函数。
3.3 tcpv4_connect_server 的调用
到这里就很清楚了,通过调用 connect_server 函数,然后根据之前指定的连接函数来触发之,由于我们在 3.2 中指定了 tcpv4_connect_server 函数,所以触发它,tcpv4_connect_server 函数中又调用了 connect 函数,所以需要跟踪 connect_server 函数。
3.4 connect_server 的调用
查看调用栈,
通过类似的调用机制,尝试定位问题。
更详细的调用栈就不一一列出。
3.4 修改源码添加自定义日志
为了定位问题的准确性,修改 HAProxy【1.4.23】源码,在每一个 session 创建和后续行为都添加了自己的日志,同时每个日志行都添加了 session 的唯一 ID.
这样就可以跟踪每个会话的具体行为。
日志格式如下:
3.5 srv_dynamic_maxconn
通过日志,我们发现,其实并不是 HAProxy 拿不到可用的服务器,而是拿到了之后,通过这个函数动态计算这个服务器当前的动态 maxconn.
跟踪下代码:
unsigned int srv_dynamic_maxconn(const struct server *s,struct session* session)
{
unsigned int max;
if (s->proxy->beconn >= s->proxy->fullconn)
{
/* no fullconn or proxy is full */
max = s->maxconn;
}
else if (s->minconn == s->maxconn)
{
/* static limit */
max = s->maxconn;
}
else
{
max = MAX(s->minconn,
s->proxy->beconn * s->maxconn / s->proxy->fullconn);
}
if ((s->state & SRV_WARMINGUP) &&
now.tv_sec < s->last_change + s->slowstart &&
now.tv_sec >= s->last_change) {
unsigned int ratio;
ratio = 100 * (now.tv_sec – s->last_change) / s->slowstart;
max = MAX(1, max * ratio / 100);
}
return max;
}
于是在此段代码中添加日志,发现在蚂蚁窝环境下,每次此函数都返回 1.
于是问题就知道出在什么地方了,这里返回 1,导致每次对于某个后端服务器来说,
第一个请求建立连接会被响应,而后续的 2,3.。。都被拒绝。
再查看日志,完全验证了这一点。
4 解决方案
既然知道了问题所在,那么怎么解决?
必然是通过此函数的逻辑来解决。
查看 srv_dynamic_maxconn 函数,发现如果在配置中可以有 2 种方法解决
1 将 minconn 设置为较大的一个参数
2 直接设置为 minconn 与 maxconn 一样,彻底去掉最小限制,对于并发量按照 maxconn 来配置。
针对第 2 种情况,代码中可以看到
也就是如果二者大小一样的话,max 就返回 s ->maxconn。这样也没有问题。
5 与 HAProxy 作者的邮件交流
既然是开源软件,那么就可以直接跟作者交流。
下面是跟作者的邮件交流。
5.1 发送邮件描述问题
5.2 对方回复
5.3 再次发送验证答案
于是发送自己的答案过去,看对方对我们的解决方案的评价,同时不忘热情赞美对方的软件之流行度。
5.4 对方的最终回应
也就是说,作者认为直接去掉 minconn 参数更好,于是我们在 haproxy.cfg 的配置中去掉了这个参数,通过日志打印,minconn 的值会等于 maxconn 参数,也就是走了 static limit 这个分支。
至此问题得以解决, 对 HAProxy 的理解比之前更进一步。
6 后记
1 碰到问题,迎难而上,尤其是在有源码的情况下,直接 debug 或者看源码,肯定可以解决问题。一般在 linux 中 c 采用 gdb,java 采用 jdb 都可逐行跟踪,非常方便准确!
2 开源软件,网上有很多别人踩过的坑,可以尝试搜索是否已经有解决方案。
3 相对于所解决的问题,方法论非常重要,这个也需要经验的积累,比如本文中 HAProxy 问题的定位其实就在于 connect(…) api 的入口定位,整理出调用栈,然后添加日志逐步定位问题。
Haproxy+Keepalived 搭建 Weblogic 高可用负载均衡集群 http://www.linuxidc.com/Linux/2013-09/89732.htm
Keepalived+HAProxy 配置高可用负载均衡 http://www.linuxidc.com/Linux/2012-03/56748.htm
CentOS 6.3 下 Haproxy+Keepalived+Apache 配置笔记 http://www.linuxidc.com/Linux/2013-06/85598.htm
Haproxy + KeepAlived 实现 WEB 群集 on CentOS 6 http://www.linuxidc.com/Linux/2012-03/55672.htm
Haproxy+Keepalived 构建高可用负载均衡 http://www.linuxidc.com/Linux/2012-03/55880.htm
使用 HAProxy 配置 HTTP 负载均衡器 http://www.linuxidc.com/Linux/2015-01/112487.htm
HAproxy 的详细介绍 :请点这里
HAproxy 的下载地址 :请点这里
本文永久更新链接地址 :http://www.linuxidc.com/Linux/2015-06/119226.htm