HAProxy高并发问题解决

HAProxy现网问题解决

1问题描述

RMI上线后,现网的接口总是报告异常。

222616_2zh4_1382024.png

 

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(....)

 

在函数

221847_WeYo_1382024.jpg

中可以看到调用了connect(...)

221914_ZtiL_1382024.png

 

3.2 tcpv4_connect_server的指定

查看tcpv4_connect_server的调用栈

221948_CfeR_1382024.jpg

222004_2pNP_1382024.png

 

 

上面这个代码是在event_accept函数中,也就是说在session中的client建立时,指定sessionserver端的connect函数,然后后面某个地方触发了tcpv4_connect_server函数。

 

3.3 tcpv4_connect_server的调用

222030_LFt9_1382024.png

到这里就很清楚了,通过调用connect_server函数,然后根据之前指定的连接函数来触发之,由于我们在3.2中指定了tcpv4_connect_server函数,所以触发它,tcpv4_connect_server函数中又调用了connect函数,所以需要跟踪connect_server函数。

 

3.4 connect_server的调用

查看调用栈,

222052_X31K_1382024.png

通过类似的调用机制,尝试定位问题。

更详细的调用栈就不一一列出。

3.4 修改源码添加自定义日志

 

为了定位问题的准确性,修改HAProxy1.4.23】源码,在每一个session创建和后续行为都添加了自己的日志,同时每个日志行都添加了session的唯一ID.

222115_202K_1382024.png

这样就可以跟踪每个会话的具体行为。

 

日志格式如下:

222136_EeVh_1382024.png

 

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.。。都被拒绝。

 

再查看日志,完全验证了这一点。

解决方案

既然知道了问题所在,那么怎么解决?

必然是通过此函数的逻辑来解决。

 

查看srv_dynamic_maxconn函数,发现如果在配置中可以有2种方法解决

minconn设置为较大的一个参数

2直接设置为minconnmaxconn一样,彻底去掉最小限制,对于并发量按照maxconn来配置。

 

针对第2种情况,代码中可以看到

222208_mTOm_1382024.png

也就是如果二者大小一样的话,max就返回s->maxconn。这样也没有问题。

 

HAProxy作者的邮件交流

既然是开源软件,那么就可以直接跟作者交流。

 

下面是跟作者的邮件交流。

5.1 发送邮件描述问题

222726_dkCe_1382024.png

5.2 对方回复

 222826_rQ9D_1382024.png

222857_eKkE_1382024.png

 

 

 

 

5.3 再次发送验证答案

于是发送自己的答案过去,看对方对我们的解决方案的评价,同时不忘热情赞美对方的软件之流行度。

222939_BmBR_1382024.png

 

 

5.4 对方的最终回应

223017_VaRE_1382024.png

也就是说,作者认为直接去掉minconn参数更好,于是我们在haproxy.cfg的配置中去掉了这个参数,通过日志打印,minconn的值会等于maxconn参数,也就是走了static limit这个分支。

至此问题得以解决,HAProxy的理解比之前更进一步。

 

后记

碰到问题,迎难而上,尤其是在有源码的情况下,直接debug或者看源码,肯定可以解决问题。一般在linuxc采用gdb,java采用jdb都可逐行跟踪,非常方便准确!

 

开源软件,网上有很多别人踩过的坑,可以尝试搜索是否已经有解决方案。

 

相对于所解决的问题,方法论非常重要,这个也需要经验的积累,比如本文中HAProxy问题的定位其实就在于connect(...) api的入口定位,整理出调用栈,然后添加日志逐步定位问题。

 

 

 

 

 

 

转载于:https://my.oschina.net/qiangzigege/blog/470431

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值