歡迎大家前往騰訊雲+社群,獲取更多騰訊海量技術實踐乾貨哦~
序言:
筆者要線上上伺服器load日誌並且重放來測一些機器效能指標。模擬機器資源比較少,相對的被模擬的線上機器日誌量大,假設線上單機qps有1w,那麼5臺機器組成的叢集5w個qps。模擬機器壓測客戶端需要比5w個qps更快,才有比較意義。
第一章:HTTP初體驗
正所謂“人生苦短,我用python”,python自帶了urllib2、urllib3以及第三方的request。支援的代理訪問、新增請求頭基本滿足功能需求。筆者用urllib2+multiprocessing庫順利了碼完程式碼執行之,檢視qps只有2k多,這顯然遠遠低於需求。在加大程式數到cpu核數的數倍之多,也發現python僅能達到3k多。事出必有因,於是筆者便通過監控介面和shell小工具來找機器各種茬。
第二章:“中世紀黑暗期”
中世紀是黑暗漫長的時期,你做了很多事情,但卻輸出很少,留下來的是嘗試後的經驗總結。從cpu、記憶體、硬碟、網路各方面資料看。cpu使用率90%多,記憶體用滿、硬碟wa很低、網路千兆網路卡滿載。最首先的是把千兆網路卡機器替換成萬兆網路卡機器。檢視timewait的連線數達到1w3多。那就先優化下看起來是"瓶頸"的東西。配置tcp_timestamps=1, tcp_tw_reuse=1, tpc_tw_recycle=1。sysctl -p生效下最新的配置,timewait連線數沒下去,併發數沒上來。既然硬體該做的設定都完了,那為什麼別人家的露娜那麼秀,我家的就是一坨屎呢。
再回過頭來考慮程式架構問題。反省自己,首先urllib2、request庫是網路io阻塞的,其次網路是短連線的,再次這麼多程式切換系統開銷也很大。在廣袤的網際網路海洋中遨遊了一番,得出的結論就是grequest庫可能是個解決辦法。gevent是個協程庫,它使用greenlet庫提供的基於libev實現的高效能非同步網路框架。Perfect!看起來是那麼的完美。於是又嘗試重寫了程式。可是效能還是沒有上去。那到底是不是python語言自身的限制問題,導致cpu高居不下,併發量又上不去呢?這裡留個疑問,到文章的最後再來回答這個問題。
第三章:豁朗開朗
不甘心並且不再糾結於python,用當下網紅golang重寫下。golang的協程庫號稱是效能優秀,語言層面支援並行的,易於書寫的利器。寫完跑一跑,併發量還是上不去。一直保持打死都不放棄的精神,筆者再次用go的第二效能利器自帶的golang pprof分析下程式碼的瓶頸。pprof生成的報告還可以用uber第三方元件go-torch生成更直觀的火焰圖。如圖1所示。從火焰圖檢視出runtime.gcBgMarkWorker(gc:垃圾回收器),並且runtime.mallocgc也佔用大量cpu時間。接著進行記憶體佔用分析,使用go tool pprof -alloc_space replay1 /tmp/mem.prof檢視如圖2 所示,敲入top10命令,發現pull_worker累加分配了600多G記憶體,佔比93%,list pull_worker命令找到該函式的瓶頸點。這個r4變數的初始化放在一個for迴圈內,r4是用於臨時讀取響應body,這個r4每次請求都重複分配,導致記憶體居高不下,解決辦法是把他放在for迴圈外。
終章:總結
好了,至此單機併發量最高可以到3w了,也差不多達到計劃的目標了。用兩臺這種機器組成的肉雞就可以滿足5w qps的請求了。再來回答之前留下來的問題,python語言併發上不去只是因為,庫不支援從外面提供讀buffer讀取響應body,導致記憶體暴增,這不是語言本身的問題。相信python並沒有那麼差。同時,也熟悉了一門新利器go語言。go的原生協程支援和效能分析利器還是非常直觀非常好用的,力薦!!
圖1:效能瓶頸前的cpu火焰圖 圖2:找到記憶體使用最多的函式 找到增長最多的程式碼問答
相關閱讀
此文已由作者授權騰訊雲+社群釋出,原文連結:https://cloud.tencent.com/developer/article/1160803?fromSource=waitui
歡迎大家前往騰訊雲+社群或關注雲加社群微信公眾號(QcloudCommunity),第一時間獲取更多海量技術實踐乾貨哦~
海量技術實踐經驗,盡在雲加社群!