半同步-半異步模式,最早應(yīng)該是由ACE的作者提出,原文在
這里.
簡而言之,所謂的半同步半異步模式分為三個組成模塊:同步處理模塊,隊列模塊,異步處理模塊.三個模塊之間的交互關(guān)系如圖:

(注:上圖出自
這里)
幾個模塊的之間的交互為:異步模塊接收可能會異步到來的各種事件(I/O,信號等),然后將它們放入隊列中,而同步模塊一般只有一種動作,就是不停的從隊列中取出消息進(jìn)行處理.
半同步-半異步模式的出現(xiàn)是為了給服務(wù)器的功能進(jìn)行劃分,盡可能將的可能阻塞的操作放在同步模塊中,這樣不會影響到異步模塊的處理.
舉個例子說明.
假設(shè)現(xiàn)在有一個服務(wù)器,在接收完客戶端請求之后會去數(shù)據(jù)庫查詢,這個查詢可能會很慢.這時,如果還是采用的把接收客戶端的連接和處理客戶端的請求(在這里這個處理就是查詢數(shù)據(jù)庫)放在一個模塊中來處理,很可能將會有很多連接的處理響應(yīng)非常慢.
此時,考慮使用半同步半異步的模式,開一個進(jìn)程,使用多路復(fù)用IO(如epoll/select)等監(jiān)聽客戶端的連接,接收到新的連接請求之后就將這些請求存放到通過某種IPC方式實現(xiàn)的消息隊列中,同時,還有N個處理進(jìn)程,它們所做的工作就是不停的從消息隊列中取出消息進(jìn)行處理.這樣的劃分,將接收客戶端請求和處理客戶端請求劃分為不同的模塊,相互之間的通過IPC進(jìn)行通訊,將對彼此功能的影響限制到最小.
然后,不是每種請求下都適合使用半同步半異步模式的.
我之前深入閱讀過ligty的代碼,它的設(shè)計是monitor+worker多進(jìn)程 + 多路復(fù)用IO + 狀態(tài)機(jī)的架構(gòu).也就是說,每個worker進(jìn)程負(fù)責(zé)接收客戶端連接和處理客戶端連接的全過程,每個過程都會記錄一個狀態(tài),比如現(xiàn)在在接收包頭,如果這次的接收不是因為連接關(guān)閉的原因?qū)е碌慕邮斟e誤,那么就將這個客戶端的fd放入多路復(fù)用IO中,等待著下一次根據(jù)這次保存的狀態(tài)進(jìn)入狀態(tài)機(jī)中進(jìn)行處理.
簡單的說,在ligty中,一個worker子進(jìn)程全權(quán)負(fù)責(zé)了接收和處理的全過程,并沒有按照上面半同步半異步的劃分來設(shè)計.
再后來,我大概看過一些nginx的代碼,細(xì)節(jié)之處可能不一樣,但是就服務(wù)器總體的架構(gòu)而言,是與ligty的設(shè)計差不多的.
這兩個服務(wù)器是目前比較快的web服務(wù)器了,沒有采用多么復(fù)雜的模式.
那么為什么對于web服務(wù)器而言,不需要使用半同步半異步也可以達(dá)到非常高的效率呢?我想,這與服務(wù)器的業(yè)務(wù)有關(guān).對于web服務(wù)器而言,大部分的時間都花在了IO處理上,比如監(jiān)聽服務(wù)器端口,接收客戶端連接,根據(jù)客戶端的請求發(fā)送文本文件內(nèi)容到客戶端去,這里的操作,基本上沒有太可能會造成阻塞的地方,也就是說,處理完成一個客戶端請求的全過程對web服務(wù)器而言是非常快的.
所以,要回答這個問題,需要看具體的業(yè)務(wù)需求.打個比方,如果處理一個客戶端請求需要10s,那么完全有一個模塊全部處理不是一個很好的設(shè)計;反之,如果處理一個請求只需要10ms,而進(jìn)程/線程間的切換就需要1s了,還將模塊進(jìn)行劃分就不必了.
另外,回到半同步半異步模式的具體實現(xiàn)上,可以使用線程或者進(jìn)程,而隊列層則可以使用不同的IPC方式,有很多關(guān)于多線程多進(jìn)程孰優(yōu)孰劣的爭論,由于我沒有太多多線程的編程經(jīng)驗,也就不在這里進(jìn)一步說明了.