HAProxy高并發問題解決
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; } </td> </tr> </tbody> </table>于是在此段代碼中添加日志,發現在螞蟻窩環境下,每次此函數都返回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的入口定位,整理出調用棧,然后添加日志逐步定位問題。 來自:http://my.oschina.net/qiangzigege/blog/470431 本文由用戶 pbpb 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!
相關經驗相關資訊 |