摘要:初步分析提升可從兩方面入手,一個是增加并發數,其二是減少平均響應時間。大部分的時間花在系統與數據庫的交互上,到這,便有了一個優化的主題思路最大限度的降低平均響應時間。不要輕易否定一項公認的技術真理,要拿數據說話。
本文最早發表于個人博客:Pylixm"Wiki
應項目的需求,我們使用tornado開發了一個api系統,系統開發完后,在8核16G的虛機上經過壓測qps只有200+。與我們當初定的QPS 大于2k差了一個數量級,于是便開始了漫長的優化之路。在優化過程中,學了許多東西,有必要整理記錄下備查。
我們的技術選型:
python2.7
tornado4.4.3
sqlalchemy1.1.5
mysql5.6
rabbitmq
當初技術選型的時候選擇tornado,便是因為其優秀的性能,這么低的QPS自然是不甘心。究竟tornado可以達到多少QPS呢?于是編寫了簡單的hello world,在上邊的虛擬機中起16個進程下,使用ab壓測QPS竟然達到了驚人的6K,平均響應時間在毫秒級。這下有信心將api的QPS繼續優化了。
初步分析提升QPS, 可從兩方面入手,一個是增加并發數,其二是減少平均響應時間。從目前情況看,增加進程并發數是最直接的手段,但當達到機器資源的瓶頸時,可靠堆疊機器來解決。那么
相比較下,減小平均響應更為重要。初步分析了我們開發的api,平均響應時間在幾百毫秒級別。大部分的時間花在系統與數據庫的交互上,到這,便有了一個優化的主題思路:最大限度的降低平均響應時間。
我們API完成的功能為,接受請求參數做一些列的認證判斷(與數據庫交互),將消息以廣播的形式發送到rabbitmq供消費者消費,最后返回給客戶端發送結果。根據此邏輯,影響響應時間的地方,分析如下:
與mysql 數據庫的交互
使用rabbitmq廣播消息時的時間耗費
耗時的業務邏輯代碼片段
優化思路根據上邊的問題,從以下幾個方面入手:
增加tornado的異步特性
分析與數據庫的交互,減少與數據庫的交互時間
分析rabbitmq的時間耗費,減少發送信息時間
優化業務代碼邏輯
具體實施 tornado 的異步特性開發api時,因為對tornado 的異步特性不是很熟悉,便沒有使用。后來隨著測試的深入,發現需要使用后,開始了解。
隨著了解的深入,發現tornado是并沒有很好的支持數據庫的異步特性,更多是對網絡的異步,官網上也是寫的”網絡非阻塞框架“。
查閱官方文檔,tornado的異步實現,見官方文檔
總的來說,使程序異步的方式有3種,參考這里。如下:
第一種,使用tornado 的 gen.coruntine。
使用此種方式,需要異步數據庫的驅動庫,經查找現階段并沒有很好的成熟的支持異步查詢mysql的python驅動,放棄此種方案。
第二種,使用tornado 的線程模塊。
此種方式比較方便,只需要在耗時的函數上添加裝飾器即可,簡單方便,可以說是一種萬能方案,但此方案耗費系統資源。 系統資源并不是我們的瓶頸,我們最后采納了此種方式。
第三種,使用外部隊列,多帶帶其worker 進程或線程去處理。例如,celery 等。
此種方式增加了外部的依賴,增加了系統的復雜性和后期的維護難度,放棄此種方案。
增加了異步特性外有顯著的提升。
mysql 數據庫的優化數據庫方便,我們適用的是SQLAlchemy。使用ORM時,在減少裸sql帶來的查詢復雜度的同時,必然會增加查詢數據庫的耗時。我們也做過測試,
使用pymsql鏈接mysql,直接使用裸sql查詢與使用sqlalcemy 的對象查詢的耗時差別有7、8個毫秒的時差,與sqlalchemy的裸sql方式執行時間幾乎一致。
可見,sqlalchemy的orm方式是有一定時間耗損的。stackoverflow的一個問題,也驗證了我的想法,見Why is loading SQLAlchemy objects via the ORM 5-8x slower than rows via a raw MySQLdb cursor?
針對數據庫方面,我們做了如下優化:
將SQLAlchemy 查詢改為核心裸sql方式,可參考這里。
優化數據庫,增加必要的索引。
將邏輯中的過濾條件,盡量的移到sql中,減少sql結果集的大小,加快查詢速度。
將可以單詞查詢出的數據集放到一次查詢中,減少鏈接數據庫的次數。
分析rabbitmq的時間耗費,減少發送信息時間rabbitmq 方面,使用的是pika 作為驅動庫連接的,使用方式是每次發送數據的時候創建鏈接和通道,發送完畢后立即關閉鏈接。考慮到是否可以使用長鏈接,創建鏈接后不關閉,只關閉channel。修改后發現報錯,具體代碼如下:
# -*- coding:utf-8 -*- import pika from settings import settings class Client(object): def __init__(self, host, port, username, pwd): self.host = host self.port = port self.username = username self.pwd = pwd self.init_connection() def init_connection(self): user_pwd = pika.PlainCredentials(self.username, self.pwd) self.connection = pika.BlockingConnection(pika.ConnectionParameters( host=self.host, port=self.port, credentials=user_pwd)) # ...
補充錯誤材料分析 - todo
翻閱了pika的文檔,發現其有異步的使用方式,且有與tornando 框架的結合的實例,見文檔。
pika的異步方式,使用了和tornado 相同的基于epull的事件循環模型,如何將其與tornado 的IOloop結合是個問題,
其有個tornado的鏈接適配器,翻看其代碼還是有些不太明確如何使用,有時間的時候再繼續研究下。
針對rabbitmq的優化我們放棄了,但優化過程中有些值得分析的文章,整理如下:
rabbitmq-amqp-channel-best-practices
rabbitmq-best-practices-for-designing-exchanges-queues-and-bindings
tornado與pika結合實例
優化業務代碼邏輯代碼邏輯方便的優化,如下:
減少循環
review 邏輯,去除冗余邏輯
提取公共變量,賦值一次,減少查詢數據庫。
總結經過以上的優化,我們的api 的 QPS 提升到了1200+, 由于時間問題,我們暫停了繼續的優化。通過本次QPS的優化過程,有幾點感悟:
使用一項新技術時,一定要認真閱讀官方文檔,了解清楚后,再使用。
不要輕易否定一項公認的“技術真理”,要拿數據說話。
個人工作總結,歡迎留言交流!文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/40750.html
摘要:這是多處理器系統中,調度器用來分散任務到不同的機制,通常也被稱為處理器間中斷,。文章編寫計劃 待完成: 詳細介紹用到的各個工具 作者: 萬千鈞(祝星) 適合閱讀人群 文中的調優思路無論是php, java, 還是其他任何語言都是用. 如果你有php使用經驗, 那肯定就更好了 業務背景 框架及相應環境 laravel5.7, mysql5.7, redis5, nginx1.15 cento...
摘要:這是多處理器系統中,調度器用來分散任務到不同的機制,通常也被稱為處理器間中斷,。文章編寫計劃 待完成: 詳細介紹用到的各個工具 作者: 萬千鈞(祝星) 適合閱讀人群 文中的調優思路無論是php, java, 還是其他任何語言都是用. 如果你有php使用經驗, 那肯定就更好了 業務背景 框架及相應環境 laravel5.7, mysql5.7, redis5, nginx1.15 cento...
摘要:展示如下場景再現經過分析,最后我們定位到是使用產生的內存泄露問題。下面通過一個,來簡單講下具體內存泄露的原因。這一次的內存泄露問題算是解決了。總結關于內存泄露問題在第一次排查時,往往是有點不知所措的。 記一次 JAVA 的內存泄露分析 摘要:本文屬于原創,歡迎轉載,轉載請保留出處:https://github.com/jasonGeng88/blog 當前環境 jdk == 1.8 ...
摘要:目前來說文章亮點就是解耦做的還行,有一定的可擴展性簡單的仿實現路由分發規定應用程序需要是一個可調用的對象可調用對象接收兩個參數可調用對象要返回一個值,這個值是可迭代的。 最近web服務器知識,中間懶癌犯了,斷了一兩天后思路有點接不上來,手頭上也有其他事情要做,先簡單的總結下學習進度,很多重要的功能都沒跑通,目前flask只是簡單實現路由分顯示不同的結果,cgi可以根據不同的靜態資源或者...
閱讀 3029·2021-11-18 10:07
閱讀 3777·2021-11-17 17:00
閱讀 2107·2021-11-15 18:01
閱讀 933·2021-10-11 10:58
閱讀 3383·2021-09-10 10:50
閱讀 3450·2021-08-13 15:05
閱讀 1232·2019-08-30 15:53
閱讀 2652·2019-08-29 13:01