高效能伺服器的分散式設計

xiaofei發表於2017-12-14

何為高效能伺服器

伺服器分類

目前的高效能伺服器,大家耳熟能詳的有很多,
這裡對通用的做了分類:
http伺服器:nginx, apache
java http容器:tomcat, jetty
java 伺服器框架: jetty, mina

其中nginx/apache/tomcat已被用於各大線上業務,按各使用場景來看功能,可概覽如下:
image.png

也可將這些功能分為基本與擴充套件。
基本功能:請求過濾,靜態處理,長短連線,規則配置,動態容器,內容壓縮。
擴充套件功能:負載均衡,反向代理, 容器。

為什麼需要擴充套件功能

概括一些,基本功能可以被視為靜態功能,擴充套件功能可被視為動態功能,所以問題被理解為為什麼需要動態功能。
我們拿一個視訊伺服器來看,其需要具備哪些功能。
網頁靜態模板, 使用者模組, 視訊分發等。

靜態伺服器僅僅滿足了靜態模板的功能, 使用者模組的鑑權與視訊分發才是重點。

回過來看下伺服器的動態擴充套件功能。

容器

這裡以nginx, tomcat為例。
image.png

tomcat為java容器而生,其上可執行各種元件與servlet。
nginx與tomcat對容器的支援方式不太一樣,nginx原生不支援容器,將其擴充套件後可執行lua指令碼,參見openresty。

tomcat 載入容器的方式:
xml/jar
xml決定配置, jar包含邏輯。
tomcat執行jar包的方式比較方便, 因為tomcat與容器內jar包都是執行在jvm之上。

nginx載入配置的方式

location /test/hello{
    default_type `text/plain`;
    charset UTF-8;
    content_by_lua_file /data/test/htdocs/hello.lua;
}

nginx執行容器其實是載入了lua執行庫。

反向代理

早期的伺服器對cgi的支援以spawn的形式支援。

通過反向代理,server可以不用關注後端連線的是什麼服務, 後端的服務崩潰了也不影響server的穩定性。

負載均衡

通過反向代理,可以使用後端的負載均衡:
image.png

通過配置規則,可以將負載均衡的負載到後面的各個點上。

有了容器,我們可以輕易定製業務邏輯。
有了反向代理與負載均衡,就可以非同步化處理請求與前後分離。

還缺點什麼

目前的主流伺服器都支援http協議,原因並不是為了方便大家用瀏覽器,而是方便約定,通用。http+https 解決了加密的問題,於是所有對外的服務
都可以用http+https協議。
但是http包有點大了,對於高密度小包的服務,不太適用http了,需要定製的tcp/udp 協議,比如protobuf,這時就產生了各種伺服器框架,如netty。

如何設計一個高效能伺服器

框架

高效能伺服器必須具備哪些素質,我們可以按網際網路2個標準來定義:
高QPS,低響應時間。

有時候我們發現這兩者有一定的關係,響應時間高的系統往往QPS低,對於同步處理可以套用公式:
QPS=1/響應時間
如果一個請求處理的平均時間為1mm,則QPS不會高於1K,假設每秒請求的次數為1W,則會形成請求堆積:
image.png

問題很大,舉一個簡單的例子,一條針對nginx請求某文字檔案的請求, 假設響應時間為2mm,那QPS豈不只有500了。
對於多個伺服器串型處理的業務,情況惡劣成:

image.png

一臺伺服器的瓶頸成為整條服務流的瓶頸。

簡單一點類比,同步請求可以理解為:

A a = funcA();
B b = funcB(a);

funcA未結束是不能進行funcB的,執行緒在funcA是阻塞的。
如果funcA是IO處理(本地IO,網路IO),目前各作業系統已經提供非同步庫。
拿linux裡的epoll為例, 可以在某fd可讀可寫時呼叫讀寫回撥,而不可讀不可寫時不佔用CPU時間。
可以理解為

func cb(A a)
{
    B b = funcB(a);
}
funcA(cb);

當呼叫funcA時,傳入回撥函式,等訊息回撥時才會處理funcB。雖然響應時間一樣,執行緒不再阻塞了,QPS提高了。
這種做法叫非同步。
非同步大法好,前面列的nginx, apache, tomcat, jetty,netty都標配非同步。
於是我們經常看到nginx讀文字的QPS達到幾萬,雖然讀一個文字的響應時間有幾個耗秒。

業務為王, 沒有最好的伺服器,只有最滿足業務的伺服器,業務如果複雜,鏈路很深,就需要進行框架級的設計。
不能讓所有業務都用一套框架,也不能為某個業務開發一個框架,但是不同的業務可以有不同的架構,在框架上可以對非業務邏輯部分進行抽象,整合,以期儘量簡單的滿足所有的業務。
所以架構應該分為框架架構與業務架構。
框架架構的目標功能:非同步、反向代理,規則配置、均衡負載

為何需要業務架構

假設我們為了處理一個業務,
業務條件:平均一個業務的處理時間為5mm,無後端資源依賴。
要求:QPS 達到1W。
變數:機器數N, CPU核M, 記憶體數K
無論N等於幾,無論同步還是非同步,我們知道必須要啟動20個執行緒或者程式,

假設業務條件改為:5個業務必須中行處理,平均一個業務的處理時間為5mm,無後端資源依賴,
QPS還是要求1W。
我們對資料整理:
5個業務的合併處理時間為25mm
每mm需要的業務處理數為10
單執行緒每秒的QPS為40,則需要250個執行緒處理,這250個執行緒分擔到N個機器,假設機器數為1, 無論同步還是非同步,單機器需要執行250個業務執行緒或者程式。
因此依靠框架的功能是解決不了具體業務的效能提升的。


相關文章