【編者按】如果你還在Symfony2和Redis使用中存在這樣的錯誤觀念――不能使用Redis作為主要存儲;Symfony2的功能很多,以至于它的運行很慢,那么不妨看向Octivi的高請求網站打造。雖然沒有底層細節,但詳細展示基于兩者應用的宏觀特性,以及開發時的Symfony2特征。
免費訂閱“CSDN大數據”微信公眾號,實時了解最新的大數據進展!
CSDN大數據,專注大數據資訊、技術和經驗的分享和討論,提供Hadoop、Spark、Imapala、Storm、HBase、MongoDB、Solr、機器學習、智能算法等相關大數據觀點,大數據技術,大數據平臺,大數據實踐,大數據產業資訊等服務。
有人說Symfony2像其它的復雜框架一樣,很慢,但是我們認為這一切都取決用戶的本身。本文將介紹基于Symfony2,每周執行10億多個請求的應用的軟件架構細節。
下面將展示tweeting之后的社交反饋:
本文將介紹基于Symfony2和Redis的應用。在此不會有過多的細節描述,相反我們將給你展示這些應用的宏觀特性,以及開發時的Symfony2特征。
對于低層次的Symfony2性能優化實踐,我們寫了專門的文章――掌握Symfony2性能系列――Internals 和Doctrine
首先是關于所描述應用的一些數據。
來自單個程序節點的性能統計:
注意,如下面所描述的,整個平臺包括許多這種節點
Redis度量:
應用
所有的流量都會流入HAProxy,HAProxy將流量分配給應用服務器。
應用實例前是Varnish Reverse Proxy。
我們保持Varnish在每個應用的服務器都保持高度可用性――沒有單點故障。單個Varnish分配流量可能導致風險。分離的Varnish實例可能降低緩存hit,不過我們可以接受這個。我們對可用性的需求高于對性能的需要,不過你可以從這些數字中看到,性能也不是什么問題。
應用的服務器配置:
數據存儲
我們使用Redis和MySQL存儲數據,它們的數字還挺大的:
我們即使用Redis作為永久存儲(用的最多的資源),又使用Redis作為MySQL上的緩存層。與典型的緩存相比,Redis存儲數據的比率很高――我們存儲1.55億多個永久類型鍵和僅500萬個緩沖鍵。實際上,我們可以使用Redis作為主要的數據存儲。
Redis配有主從設置。通過這種方式我們獲得HA――如果發生運行中斷我們可以很快的將主節點切換到某一個從節點。一些管理任務如升級也需要這些配置。在升級節點時,我們可以選擇新的主節點,然后升級先前的主節點,最后交換兩個節點。
我們仍在等待生產就緒的Redis集群,這些集群可以提供類似自動故障恢復(升級節點時即使是手動故障恢復也會方便的多)的功能。不過目前還沒有任何關于官方發布日期的消息。
MySQL通常用作非耗盡資源的第三層緩存層(Varnish > Redis > MySQL)。所有的表都是InnoDB,最多的查詢是簡單的
SELECT ... WHERE 'id'={ID}這個查詢返回單個結果。我們還沒有發現這么設置會有什么性能問題。
與Redis設置不同,MySQL運行在主配置上,除高可用性外,這還提供了更好的寫性能(在Redis中這不是什么問題,因為我們不會耗盡性能特性。)
Application’s Architecture
Symfony有一些很棒的功能,這些功能使開發過程變得更容易,下面我們紹開發者最喜歡的一些功能:
注釋
我們使用帶注釋的Symfony2標準分布:
因為應用用作REST API,所以我們主要不使用模板(例如Twig)。我們保留模板主要是為了一些內部的儀表盤面板。
我們還沒有發現不同的配置類型(YAML/XML)帶來的性能影響。因為所有的注釋都很好的存儲下來了,所以沒有什么令人費解的地方―最后所有的東西都是純PHP代碼。
下面是我們使用JMSDiExtraBundle獲得的服務配置樣例:
/** * Constructor uses JMSDiExtraBundle for dependencies injection. * * @InjectParams({ * "em" = @Inject("doctrine.orm.entity_manager"), * "security" = @Inject("security.context") * }) */ function __construct(EntityManager $em, SecurityContext $security) { $this->em = $em; $this->security = $security; }
通過這種方式,改變類依賴項只需要改變代碼。
Symfony2監控―Monolog和Stopwatch
應用使用Monolog記錄意料之外的行為,捕獲錯誤信息。我們使用多個信道獲取不同應用模塊的分離的日志。
因為FingersCrossed handler使用較多內存(可能導致內存泄漏),所以我們不再使用它。我們選用適當的StreamHandler。使用這種方式時我們需要在單行日志信息添加冗余和額外的內容。
我們也在很多地方使用Stopwatch組件以控制一些典型的應用方法。通過這種方式我們可以發現客制化邏輯一些大塊中的弱點。
例如,我們追蹤一些外部網絡服務的請求次數:
if (null !== $this->stopwatch) { $this->stopwatch->start('my_webservice', 'request'); } // Makes a CURL request to some my_webservice $response = $this->request($args); if (null !== $this->stopwatch) { $this->stopwatch->stop('my_webservice'); }
控制臺組件
開發和維護時,我們特別喜歡Symfony控制臺組件,這個組件為創建CLI工具提供了很好的面向對象接口。應用大概添加了50%的新功能,這些新功能基于CLI指令,主要用作管理或分析應用內部構件。
控制臺組件妥善的處理命令語句或選項―你可以設置默認值,可選值或所需的值。好的實踐總是將這些恰當的記錄為代碼―你可以給命令和選項設置主要描述。命令通常是自我文檔的,因為添加--help選項便能生成格式化的指令描述。
$ php app/console octivi:test-command --help Usage: octivi:test-command [-l|--limit[="..."]] [-o|--offset[="..."]] table Arguments: table Database table to process Options: --limit (-l) Limit per SQL query. (default: 10) --offset (-o) Offset for the first statement(default: 0)
我們必須牢記在準確設置的環境下運行指令。默認的dev可能會導致一些問題,如內存泄漏(因為更多冗長的日志存儲和保存調試信息)。
$ php app/console octivi:test-command --env=prod
想要更好的信息顯示,添加-v選項。
$ php app/console octivi:test-command --env=prod -vvv
進度條是一個很好的幫手。進度條甚至考慮了信息顯示詳細程度,當程度比較低時,只顯示基本信息,程度比較高時,還可以顯示運行時間,內存消耗等信息。
此外,我們還有一些耗時大約兩天的遷移過程―0內存泄漏―沒有進度條,監控它們將是災難。
數據層
對于Redis,數據層我們使用PredisBundle。
我們拒絕Doctrine ORM,因為它將添加額外費用,而且我們不需要任何高級的面向對象操作。我們使用Doctrine DBAL代替,Doctrine DBAL特征如下:
使用PredisBundle和Doctrine Bundle也允許我們在大量使用分析工具的時候監控弱查詢。
多虧Symfony2,這種設置在保持高性能和高可用性的同時保持了友善的開發環境――可維持,穩定。實際上這是用作電商網站的關鍵子系統的關鍵業務需求。
因此本文的最后我們可以糾正一些錯誤觀點:
原文鏈接:Handling 1 Billion requests a week with Symfony2(編譯/ 蔡仁君 責編/仲浩)