IM需要在其中一個分區進行線上全鏈路壓測,模擬100w終端用戶連接,並根據線上各websocket和http請求按照比例qps x2併發請求。通宵達旦趕項目1周,但是很多問題仍未找到答案,不過先記錄mark一下過程的問題
測試鏈路
jmeter - SLB(2) - nginx(4) - ingress(5) - pod - im服務
測試場景
- 模擬100w終端保持心跳並接收消息
- 模擬ws和http羣聊(發送羣聊,廣播消息到該羣組中所有用戶)
- 模擬其他非廣播ws和http請求
單肉雞模擬終端連接數
在一臺8核 16g的肉雞中,可以模擬多大的終端數?
-
模擬2w連接時,連接數無法上升到2w,僅能連接成功6k左右,jmeter返回大量的
end of stream
![!](https://img-blog.csdnimg.cn/20200314042221303.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xsdW96aDIwMTU=,size_16,color_FFFFFF,t_70) -
調整肉雞的配置信息
net.ipv4.tcp_max_tw_buckets = 200000
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 11000 61000
fs.file-max = 1000000
net.ipv4.ip_conntrack_max = 2000000
net.ipv4.netfilter.ip_conntrack_max = 2000000
net.nf_conntrack_max = 2000000
net.netfilter.nf_conntrack_max = 2000000
net.ipv4.tcp_max_orphans = 500000
net.ipv4.tcp_mem = 786432 2097152 3145728
net.ipv4.tcp_rmem = 4096 4096 16777216
net.ipv4.tcp_wmem = 4096 4096 16777216
此時連接數可以上到1w
-
保持1w的終端連接,可以長時間的正常連接並保持心跳,但在接收羣聊廣播的消息時,仍出現
end of stream
和16g內存跑滿等問題 -
同時在模擬發送請求時,出現jmeter 出現
WebSocket I/O error: Read timed out
的報錯信息,將源碼打印日誌並build打包時,又出現了另外一個local class incompatible
的錯誤
增加serialVersionUID的強制定義private static final long serialVersionUID = -4014547742856395088L;
,發現仍然報此錯誤,後面發現是因爲控制端和肉雞的ws jar包版本不一致導致此問題的出現 -
在JMeter WebSocket Samplers源碼issue中作者也提及這些問題在1.2.26版本github issue中已解決
確實有一定的改善,但是模擬1.5w終端時仍有少量的錯誤 -
此時連接數仍不能達到1.5w,從鏈路中排查問題,發現單SLB個連接數能達到1w,使用2個SLB連接數仍然爲1w,定位是此時連接的是阿里外網的SLB導致
-
模擬1.5w終端連接後,模擬ws發送羣聊消息,各個終端接收廣播的消息,jmeter又出現
end of stream
和read timeout
的問題,定位問題可能是因爲讀幀的問題
在jmeter腳本中增加WebSocket Single Read Sampler
接收數據包,上面問題得到一定的緩解,但仍會出現 -
單個肉雞模擬1w終端連接,試驗後並沒有問題,可正常保持心跳並接收廣播信息,穩妥起見,最終決定單個肉雞模擬1w連接
100w終端連接
- 模擬50w終端連接(50臺肉雞,每臺模擬1w終端),使用的是GUI模式,此時控制端卡死,只能在各個肉雞非GUI模式全部運行
JVM_ARGS="-Xms6g -Xmx6g" /root/apache-jmeter-5.1.1/bin/jmeter -n -t /root/apache-jmeter-5.1.1/bin/im_enter_room.jmx &
-
模擬50w終端連接,模擬ws羣聊(tps2200,廣播下行包數量3000w/min),長時間運行(>1h)並沒有任何問題(此時jmeter、nginx、服務均正常)
-
在步驟1的基礎上,增加連接50w用戶,此時新增的50w用戶僅連接成功25w左右,多次試驗均爲此數值,排查發現一個終端nginx需要保持2個連接,每個連接需要4個worker_connections,現在有8個8核的nginx(每個SLB4個),nginx中worker_connections的值爲10w,那麼能夠支持的連接數剛好是80w,將worker_connections的值修改爲100w,連接數可正常達到100w
如果nginx 中worker_connections 值設置是1024,worker_processes 值設置是4,按反向代理模式下最大連接數的理論計算公式:
最大連接數 = worker_processes * worker_connections/4
-
正常連接100w終端後,進行模擬ws羣聊時,出現大批量的掉線,nginx在短時間內OOM
且jmeter端報錯爲
there is no connection to re-use 2020-03-12 00:02:09,587 ERROR e.l.j.w.SingleReadWebSocketSampler: Sampler 'WebSocket Single Read Sampler': there is no connection to re-use
-
經過理論和實踐,對配置信息進行調整如下
proxy_buffer
針對單個連接的資源配額需要管控,一個最快改動方式是把 proxy_buffering 設置爲 off。在壓測環境修改了這個值以後,以及調小了 proxy_buffer_size的值以後,內存穩定在了 20G 左右,沒有再飆升過。後續可以開啓 proxy_buffering,調整proxy_buffers 的大小可以在內存消耗和性能方面取得更好的平衡
worker_connections
前面也提到將worker_connections的值設置爲100w,但是100w需要預先分配大量的內存,將nginx升級配置16核,並將worker_connections的值設置爲10w,按照計算可以支撐160w連接
配置爲1個SLB(支持100w併發連接)-> 4個nginx(32C 64G) ->5個ingress(32C 64G)
nginx的net.ipv4.ip_local_port_range
服務器某個端口可以連接的最大tcp數量是由四元組成:src_ip(本地ip),src_port(本地端口),dst_ip(客戶端ip),dst_port(客戶端端口)
理論上在src_ip,src_port固定的情況下,一個客戶端Ip理論上最多有65535個連接數。ip_local_port_range這個參數控制的就是dst_port的範圍
4個nginx和5個ingress,那麼將dst_port的值設置爲1024-65536,理論上可以支撐的連接數(65535-1024)* 5 *4 = 120w
worker_rlimit_nofile
進程的最大打開文件數限制,這樣nginx就不會有“too many open files”問題了。不能超過最大文件打開數在linux終端中輸入ulimit -a進行查看
業務請求
可進行ws羣聊(tps2200)和http羣聊(wps1000)測試,但增加ws獲取房間歷史消息和獲取成員列表的接口請求時,,nginx內存飆升,短時間內OOM
抓包發現jmeter肉雞出現大量零窗口,同時使用命令 ss -nt
發現每個端口緩存在持續飆升,這兩個接口返回的數據量太大導致jmeter發送請求端肉雞端口緩存飆升,同時拖垮nginx,降低請求tps已經增加等待時間等操作後,可正常請求,並達到預期的效果