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

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