qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請(qǐng)?jiān)L問(wèn) http://qaseven.github.io/

          百萬(wàn)連接之路

            前段時(shí)間接到某項(xiàng)目中關(guān)于虛擬機(jī)所在宿主機(jī)上最大支撐連接數(shù)的測(cè)試需求。應(yīng)用場(chǎng)景類似于在物理機(jī)上運(yùn)行著多個(gè)虛擬機(jī),這些虛擬機(jī)對(duì)外提供服務(wù),來(lái)自于任何地方的客戶端都可能向這些應(yīng)用服務(wù)發(fā)起連接和請(qǐng)求。也許單個(gè)虛擬機(jī)并發(fā)的連接數(shù)十分有限,但對(duì)提供虛擬機(jī)服務(wù)的物理機(jī)或宿主機(jī)來(lái)說(shuō)連接數(shù)就可能達(dá)到十萬(wàn)、幾十萬(wàn)甚至百萬(wàn)。在這樣的情況下,宿主機(jī)是否能夠穩(wěn)定運(yùn)行呢?同時(shí)項(xiàng)目方也提出了明確的測(cè)試目標(biāo),支撐300萬(wàn)連接,這就需要我們著手進(jìn)行測(cè)試驗(yàn)證了。
            首先,我們先做一個(gè)假設(shè),即這300萬(wàn)連接里同時(shí)只有少數(shù)活動(dòng)連接,測(cè)試場(chǎng)景可以簡(jiǎn)化為保持300萬(wàn)長(zhǎng)連接的測(cè)試。明確了這一點(diǎn),我們繼續(xù)分析測(cè)試可能出現(xiàn)的瓶頸點(diǎn),是CPU、內(nèi)存還是網(wǎng)絡(luò)?借鑒以往長(zhǎng)連接或者消息推送類服務(wù)的測(cè)試經(jīng)驗(yàn),由于保持長(zhǎng)連接并不需要消耗過(guò)多CPU和網(wǎng)絡(luò)資源,只是占有系統(tǒng)的內(nèi)存,我們決定把關(guān)注點(diǎn)主要集中在內(nèi)存使用上,測(cè)試過(guò)程中也確實(shí)出現(xiàn)了一些內(nèi)存相關(guān)的問(wèn)題,這是后話了。
            我們首先需要準(zhǔn)備一個(gè)測(cè)試環(huán)境,一個(gè)可以支撐足夠連接的應(yīng)用服務(wù),大量的客戶端。很幸運(yùn)我們可以借用杭研前臺(tái)技術(shù)中心基于node.js開(kāi)發(fā)的開(kāi)源游戲引擎pomelo作為服務(wù)端程序。使用java網(wǎng)絡(luò)API開(kāi)發(fā)了一個(gè)簡(jiǎn)單的客戶端程序。客戶端向服務(wù)器發(fā)起連接請(qǐng)求,成功后把連接對(duì)象保存在內(nèi)存中。至此萬(wàn)事具備,只欠執(zhí)行測(cè)試了。
            不急,我們先建立100萬(wàn)連接試試。沒(méi)過(guò)多久第一個(gè)攔路虎就出現(xiàn)了,在客戶端日志里出現(xiàn)了java.net.NoRouteToHostException異常,將它作為關(guān)鍵字google一把,原來(lái)是/proc/sys/net/ipv4/ip_local_port_range配置的區(qū)間太小,端口耗盡導(dǎo)致,配置修改如下
          pomelo@debian:~/socktest$ cat /proc/sys/net/ipv4/ip_local_port_range
          2048 65500
            可見(jiàn)單個(gè)客戶端ip只能建立6萬(wàn)多連接,所以我們需要大約50個(gè)獨(dú)立ip發(fā)起300萬(wàn)的連接,為簡(jiǎn)單起見(jiàn)我們使用50臺(tái)虛擬機(jī)運(yùn)行客戶端,而沒(méi)有采用單機(jī)多ip的方案。
            繼續(xù)測(cè)試,再次被異常打斷,java.net.SocketException: Too many open files。這個(gè)有經(jīng)驗(yàn)的同學(xué)都應(yīng)該了解,該修改ulimit了。改完繼續(xù),可奇怪的是運(yùn)行了一段時(shí)間后,又被同樣的異常中斷了,不是修改得很大了嗎?怎么又出了呢?確認(rèn)修改有沒(méi)有生效,/etc/security/limits.conf文件是否保存,不會(huì)是這么狗血的問(wèn)題吧。確認(rèn)已經(jīng)保存,檢查了客戶端進(jìn)程的limit,/proc/pid/limits,發(fā)現(xiàn)open files竟然只有4096。百思不得其解。最后還是SA發(fā)現(xiàn)了問(wèn)題的原因,原來(lái)是open files默認(rèn)最大值1024*1024,我們修改時(shí)設(shè)置過(guò)大導(dǎo)致其溢出了,最后在/etc/security/limits.conf中添加如下兩行搞定
          * hard nofile 1048576
          * soft nofile 1048576
            至此我們終于完成了100萬(wàn)連接的階段性勝利,不能松懈, 一鼓作氣拿下300萬(wàn)大關(guān)。等等,有人可能會(huì)問(wèn),服務(wù)端的open files也是配置1048576,就100萬(wàn)多點(diǎn),肯定不能支撐300萬(wàn)連接吧。是的,open files是針對(duì)單一進(jìn)程的限制,但我們跑的服務(wù)是多進(jìn)程程序,所以不用擔(dān)心。另外,open files的最大值也能通過(guò)配置/proc/sys/fs/nr_open參數(shù)修改,這樣就能擺脫1048576的上限了;而系統(tǒng)中所有進(jìn)程打開(kāi)的文件數(shù)確實(shí)是需要配置的,通過(guò)fs.file-max修改。
            接著我們遇到了這次測(cè)試中最可疑的問(wèn)題之一。當(dāng)建立完200萬(wàn)連接以后,我們kill掉了一個(gè)服務(wù)進(jìn)程,沒(méi)過(guò)多久,就發(fā)現(xiàn)部分運(yùn)行客戶端的虛擬機(jī)不能ssh登陸了。通過(guò)vnc連接上后發(fā)現(xiàn)虛擬機(jī)CPU幾乎跑滿了,dmesg中存在Out of socket memory這樣的錯(cuò)誤信息。此處省略一千字某SA大神的問(wèn)題定位過(guò)程,本質(zhì)上是由于服務(wù)端和客戶端之間存在4萬(wàn)連接,服務(wù)進(jìn)程掛掉后客戶端機(jī)器收到大量FIN包導(dǎo)致網(wǎng)絡(luò)相關(guān)內(nèi)存溢出,而客戶端機(jī)器上存在一個(gè)使用curl的定時(shí)任務(wù),網(wǎng)絡(luò)內(nèi)存溢出引發(fā)其某個(gè)bug,進(jìn)而引發(fā)CPU跑滿。因此調(diào)大net.ipv4.tcp_mem配置,注意該參數(shù)的單位是頁(yè),而不是字節(jié)。
            眼看著300萬(wàn)連接的目標(biāo)已經(jīng)近在咫尺,可問(wèn)題再次不請(qǐng)自來(lái)了。在漫長(zhǎng)的等待后,我們用ss –s確認(rèn)最終建立的連接數(shù),但這個(gè)數(shù)值卻始終停留在280萬(wàn)附近,照例的打開(kāi)dmesg查看,發(fā)現(xiàn)了一大堆錯(cuò)誤信息,如下為開(kāi)始的一段
          [531503.634391] TCP: too many of orphaned sockets
          [531503.634412] TCP: too many of orphaned sockets
          [531503.634432] TCP: too many of orphaned sockets
          [531503.634451] TCP: too many of orphaned sockets
          [531508.704084] net_ratelimit: 255864 callbacks suppressed
          [531508.704088] Out of socket memory
          [531508.704233] Out of socket memory
          [531508.704245] Out of socket memory
            簡(jiǎn)單的調(diào)大net.ipv4.tcp_max_orphans參數(shù),問(wèn)題依舊。查看服務(wù)端日志,發(fā)現(xiàn)報(bào)錯(cuò)
            FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory
            查看服務(wù)進(jìn)程數(shù),確認(rèn)有部分進(jìn)程已不存在了,結(jié)合以上兩種日志不難猜到問(wèn)題的根源,是由于服務(wù)進(jìn)程掛掉,瞬間出現(xiàn)大量孤兒連接,進(jìn)而導(dǎo)致網(wǎng)絡(luò)內(nèi)存溢出引起。但服務(wù)進(jìn)程為什么會(huì)莫名其妙地掛掉呢?咨詢了相關(guān)開(kāi)發(fā)人員,由于pomelo是基于node項(xiàng)目的,需要通過(guò)編譯選項(xiàng)調(diào)整內(nèi)存上限,修改重新編譯后測(cè)試,進(jìn)程還是出現(xiàn)了內(nèi)存溢出的情況,但明白了問(wèn)題的本質(zhì),我們通過(guò)增加node進(jìn)程數(shù)量,以減小單個(gè)進(jìn)程的內(nèi)存占用,可以繞過(guò)該問(wèn)題。而node進(jìn)程的內(nèi)存限制問(wèn)題還需要后續(xù)的確認(rèn)。
            到此為止,終于完成了300萬(wàn)連接的目標(biāo),可喜可賀。簡(jiǎn)單總結(jié)一下,連接數(shù)測(cè)試不同于常規(guī)的性能測(cè)試,不太關(guān)注TPS和響應(yīng)時(shí)間等指標(biāo),主要是通過(guò)dmesg和日志中出現(xiàn)的異常信息定位問(wèn)題,進(jìn)而調(diào)整系統(tǒng)相應(yīng)參數(shù),這些參數(shù)大多與網(wǎng)絡(luò)、句柄數(shù)及內(nèi)存有關(guān)。不僅僅是測(cè)試階段,日常運(yùn)維這類系統(tǒng)也應(yīng)該時(shí)刻關(guān)注這些。
          相關(guān)文章
          百萬(wàn)連接測(cè)試之方法篇

          posted on 2013-12-13 09:32 順其自然EVO 閱讀(604) 評(píng)論(0)  編輯  收藏 所屬分類: web 前端性能測(cè)試

          <2013年12月>
          24252627282930
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 浏阳市| 凉城县| 桦川县| 营山县| 滨州市| 永昌县| 晋中市| 马鞍山市| 古蔺县| 乌拉特中旗| 景洪市| 桦甸市| 湘潭市| 高密市| 临澧县| 镇雄县| 阳春市| 澄迈县| 湖南省| 霸州市| 嘉黎县| 随州市| 文水县| 都安| 正安县| 黑山县| 重庆市| 兴和县| 德江县| 准格尔旗| 丰顺县| 海城市| 富源县| 大兴区| 珠海市| 酒泉市| 英吉沙县| 克山县| 静安区| 富裕县| 太谷县|