簡單談一談大並發服務器框架設計的基本思路
基本的服務器框架都是C/S結構的,請求和相應流程是這樣的:
這樣的框架存在一個很嚴重的問題,當客戶端大並發請求到來,服務器需要進行大量的數據庫操作,假設數據庫最大連接數為
1000個,此時有10000個請求訪問應用服務器,那么應用服務器只能處理1000個請求,剩下99000個等待1000個請求處理好后
再進行訪問數據庫處理。可以在應用服務器和數據庫服務器中間增加中間層DAL,DAL采用緩沖隊列和連接池設計。
DAL設計緩沖隊列,存儲等待的請求,並且DAL中設計數據庫連接池,當數據庫連接池中有空閑連接,
那么從緩沖隊列中取出一個請求處理,以此類推。這種做法有效的降低了服務器的壓力,但是沒有提高處理速度,
僅僅保證了請求被緩存,處理效率仍受限於數據庫的並發數。那么可以再增加一層緩存,將常用的數據加載如緩存,
有請求到來時,應用服務器先從緩存中獲取數據,如果緩存中有數據,那么不需要訪問數據庫,如果緩存中沒有,
在訪問數據庫取出數據,並更新緩存。
緩存如何同步?
有兩種手段:
第一種方法: 緩存是具有時效的,在一定時間過后會超時timeout,如果緩存失效,那么重新去數據庫查詢,
查詢后更新緩存,這種方法不是實時的,實時性比較差。
第二種方法:當有請求修改數據時,更新緩存,並且將要修改的數據投入DAL層,當數據庫有空閑連接時,再持久化
存盤。
緩存的不足之處:
當緩存足夠多時,需要將不活躍緩存數據換出內存,叫做緩存換頁。緩存換出算法和操作系統換頁算法類似,FIFO,LRU(least recently used),
LFU(least frequently used)等。實際緩存的實現不需要自己去實現,有很多開源技術,nosql技術就是非關系型數據庫的意思。
非關系型數據庫如redis,memcatched等。緩存可以跟應用服務器部署在同一台機器上,也可以部署在單獨機器上。我推薦將緩存服務器部署在
單獨機器上,假設有兩台應用服務器,如果將緩存部署在不同的應用服務器上,那么不同的應用服務器很難訪問彼此的緩存,非常不方便。將緩存
部署在單獨服務器上,各個應用服務器都能訪問該緩存服務器。
如果有大量的業務請求到來,雖然設計了多個應用服務器,也架設了緩存服務器,完善了中間層的緩沖隊列和數據庫連接池,
但是數據庫服務器仍然會出現瓶頸。比如當有大量復雜的寫操作數據庫,很多讀數據庫的操作就被阻塞了,為解決這個問題可
將數據庫實現讀寫分離。由於數據庫讀操作會比寫操作多,那么可以對數據庫執行負載均衡。主流數據庫都有replication機制,
采用replication機制可以實現負載均衡。中間層的寫數據庫操作投遞到master數據庫中,讀操作從slave數據庫中讀取,
當master數據庫中數據被修改后,數據庫采用replication機制將數據同步給slave服務器。
同樣的道理,應用服務器也可以實現負載均衡,架設多個應用服務器,不同的請求分配給不同的應用服務器。
可單獨設計一個任務服務器監控各個應用服務器的負載情況,合理的分配任務給各個應用服務器。這種方式
是任務服務器主動地分配任務給應用服務器,應用服務器被動的接受任務,這種方式在任務請求類型相近的
情況下,分配方式非常合理。但是假設應用服務器A接受了3個任務,應用服務器B接受了5個任務,按照負載均衡的
權重法或最小連接法,肯定會分配給A任務,但是如果這3個任務都是復雜的寫操作,而B的5個任務都是簡單的
讀操作,那么這就存在分配的不合理性,如何解決這個問題呢?
可以換一種思路去解決這個問題,讓應用服務器主動去請求任務服務器,主動獲取任務處理,如果應用服務器處於忙碌狀態就不需要
請求新的任務,空閑的應用服務器會去請求任務服務器中的任務,這是最合理的負載均衡。如果所有應用服務器都處於忙碌狀態,
那么任務服務器將任務緩存至自己的任務隊列,當應用服務器空閑時會來取任務。
考慮這樣一個問題,如果任務服務器出現故障怎么辦?
任務服務器需要有多台,並且實現failover機制,多台任務服務器之間實現心跳,如果檢測不到對方心跳,則使自己成為主任務服務器。
到目前為止,這個框架可以適用於大部分服務器邏輯。為保證數據庫的響應速度和處理效率,可以對數據庫進行分區。
數據庫分區有兩種形式(分庫、分表)
分庫:數據庫可以按照一定的邏輯把表分散到不同的數據庫。這叫做垂直分區,就是所每個庫的表不同,功能不同。
這樣做不常見,因為很大情況下,數據庫中各個表是關聯的,如果將不同的表分配到不同的數據庫中,會存在很多不便。
分表:將一個表的不同數據分配到各個數據庫,這樣每個數據庫的表結構是一樣的,只是存儲的用戶數據不同而已,叫做水平
分區。分表的方式很常見,如果數據庫的壓力增加,我們就采取分表的方式減少數據庫的壓力。
另外服務器開發的幾個性能殺手:
1 數據拷貝,數據從內核態copy到用戶態,或者在用戶態之間copy會造成性能損失,盡量采用緩存的方式解決。
2 環境切換 ,多線程上下文切換造成開銷。如果服務器是單核的,那么采用狀態機方式單線程效果最佳。如果是多核的,
合理采用多線程,可以提升性能。
3 內存分配,可以采用內存池,提前分配。
4 鎖競爭,加鎖解鎖會造成一定的效率衰減。
到此為止,服務器框架介紹完畢,謝謝關注我的微信公眾號: