前段時間又重新看了一遍socket編程,心血來潮寫了一個mini型的HTTP服務(wù)器,這就是monkv。
monkv GitHub地址:https://github.com/cuihang/monkv
古人說的好,紙上得來終覺淺,絕知此事要躬行。很多東西的原理,大家都能侃侃而談,但具體操作起來,會面臨各種各樣棘手的小細節(jié)。真正的高手,講起大道理來未必有什么過人之處,因為大的道理誰都會說,真正的高手體現(xiàn)在對具體細節(jié)的處理上。所以在這里我記錄一下在monkv開發(fā)過程中出現(xiàn)的各種小問題,作為自己的一種技術(shù)積累,也分享給大家,權(quán)作交流。
如何設(shè)置epoll
epoll有兩種模式,ET和LT模式。至于具體的區(qū)別,網(wǎng)上遍地都是講解,這里不展開,這里為什么會專門提到epoll模式的問題,因為在開發(fā)過程中,面臨一個小問題,就是如何保證一個請求只被一個線程或者進程解析。
就算沒有看過monkv的源碼,也能猜到monkv的大體架構(gòu),無非是主進程或者主線程監(jiān)聽epoll上的客戶端socket可讀事件,如果有新的客戶端可讀事件,就新開一個進程或者線程來處理,這是最傳統(tǒng)的多路復(fù)用模型。那么問題來了,假設(shè)一個客戶端上有一個可讀事件,我們新開一個線程來處理。然后這個客戶端上又發(fā)生了一個可讀,如果我們再開一個線程,就意味著兩個線程同時在處理一個請求,這就需要涉及到復(fù)雜的同步機制。所以最好的辦法就是保證一個請求只能被一個線程來處理。
這種情況下怎么辦?可能有人會說設(shè)置et模式呀,請注意,單純設(shè)置et模式也無法解決這個問題,因為et模式下,客戶端新的可讀事件也會被監(jiān)聽到。
最后的解決方案就是設(shè)置EPOLLONESHOT。具體可以google一下EPOLLONESHOT的用法。
如何保存客戶端的解析狀態(tài)
網(wǎng)絡(luò)傳輸是完全不可控的。什么情況都可能發(fā)送。正常情況下,http報文會及時完整的傳輸?shù)絪erver端。server端進行解析就可以了,但如果server端得到的http報文是殘缺的,又該如何解析呢?
注意一個問題,這里的殘缺不是指順序紊亂或者缺失,tcp是可靠的,這里的殘缺值得是在某個監(jiān)聽事件里面得到的數(shù)據(jù)是殘缺的,比如說某個可讀事件上的數(shù)據(jù)是"GET / HT",很明顯,這是個殘缺的請求報文。并且可以猜想,該socket上下一個監(jiān)聽事件讀出來的數(shù)據(jù)肯定是"TP/1.1\r\n......."。這種情況該如何處理呢?
所以需要保存解析狀態(tài),需要搞一個類似狀態(tài)機的東西。否則就像剛才的情況,明明是一段正確的報文,但兩次監(jiān)聽解析都顯示報文錯誤。
還有一個問題,是狀態(tài)機該如何保存的問題。epoll_event結(jié)構(gòu)體中標識socket的字段是data,而data是一個union,包含標識文件描述符的fd和標識指針的ptr。
指針是最方便的,可以指向一個自定義的request結(jié)構(gòu)體,將讀取的數(shù)據(jù),解析的狀態(tài)都保存在request中。
如果用fd也行,那么需要單獨維護一個fd到狀態(tài)機的映射關(guān)系,這樣根據(jù)這個fd就能確定該socket上的數(shù)據(jù)解析到哪一步了。
線程池的實現(xiàn)
一般不建議用多進程,因為進程的開銷太大。從效率角度來說,采用多線程比較好,但一個請求開一個線程,請求結(jié)束銷毀線程,這樣的開銷也有一點大,最好的方式就是用線程池。
網(wǎng)上很多線程池的實現(xiàn)代碼。主要是通過互斥鎖和條件變量的方式實現(xiàn),monkv中的線程池也是這樣實現(xiàn)的。有興趣可以研究一下。
下一步的工作
monkv只是一個非常mini的server,目前只能解析get方法,只能處理靜態(tài)資源,與其說是一個server,倒不如說是我目前的一個小玩具,離生產(chǎn)環(huán)境還有遙不可及的距離。其實寫monkv就是寫著玩,就是要做一個小模型玩具,只是說目前的monkv是一個粗糙的模型,我想把monkv細細打磨,通過寫著玩來提高自己的網(wǎng)絡(luò)編程能力。所以下一步想做以下工作
- 替換monkv的http解析方法,使之能夠解析更多的http方法
- 添加對php lua的支持,使之能夠處理動態(tài)腳本
- 如果一切順利,還希望能夠做成多進程,類似nginx那種派生出多個子進程的架構(gòu)。不過這個就更加復(fù)雜了,目前在想如何處理驚群效應(yīng),能力有限還是沒有太好的方案
想來想去,目前暫時就是這些問題。雖然monkv是我自己親手寫的,但我對monkv背后的技術(shù)真的理解嗎?我想我還是不理解的,很多東西就是這樣,你以為你明白了,再過些年回頭看,你還是沒明白,你之所以覺得自己明白,只是很巧合某些東西沒有被暴露出來而已。任何一個技術(shù)點背后都有很多很多的細節(jié)。所以還是那句話,紙上得來終覺淺,絕知此事要躬行。