一開始,我得向Libuv庫和Libuv庫開發者以及相關粉絲們道一個歉,對不起,我錯怪你們了。深深感到自己的無知,是多麼羞愧的事情!! 事情的經過是這樣的。 原先按照公司要求,我在開發Windows版的TCP伺服器時,使用了Libuv庫。正是因為Libuv庫的強大,才讓我們老大推薦使用。我們老大學識淵 ...
一開始,我得向Libuv庫和Libuv庫開發者以及相關粉絲們道一個歉,對不起,我錯怪你們了。深深感到自己的無知,是多麼羞愧的事情!!
事情的經過是這樣的。
原先按照公司要求,我在開發Windows版的TCP伺服器時,使用了Libuv庫。正是因為Libuv庫的強大,才讓我們老大推薦使用。我們老大學識淵博,閱歷豐富,他的推薦自然也是很值得使用的。所以我快速學習了一下Libuv庫的使用。然後再學習的過程中,稍有瞭解。同時發現了一個網友phata寫的對於Libuv庫的包裝,讓代碼寫起來更加方便。當然,他是針對Windows版的包裝。我用了之後,也就大大加快了開發的速度。對此,我非常感謝網友phata。也正是這些網友的無私,將一些寶貴的代碼分享出來,才促進了開發的發展,讓學習開發變得容易。所以我一直堅持分享的習慣,也正是更多是受益於各路網友的分享,我學有所成,我也希望將我的經驗和成果分享給更多人。互聯網的共用互助精神大概如此吧。C++技術網就是我分享我所有經驗的平臺,希望更多人能夠參與進來吧。
Windows版的伺服器做好之後,後來又需要Linux版的伺服器。所以,直接將libuv使用在了Linux的Centos發行版上了。phata編寫的Windows版本libuv封裝類,我進行了精簡整理,發佈在C++技術網。然後我再將這個精簡後的版本,改成了Linux版本,並應用在了Linux版的伺服器上。Linux版的Libuv封裝類的代碼見《基於libuv封裝的TCP通信類-服務端類源代碼》。在這個文章里,你可以找到其他相關的代碼。
然而好景不長,在後續的測試中發現了一個問題:客戶端發送一個數據到伺服器後,伺服器單次回覆一次數據,一切正常。但是收到一個數據,連續回覆兩次數據時,不管是間隔1秒還是10秒,都會造成線程死迴圈。這個問題持續了很久,而且只在Linux中表現出來。
遲遲沒有直接解決問題,所以後來就想到一個辦法,就是將多個數據合併到一個數據,然後將多次發送做成了一次發送,這樣就避開了問題。然而再後來出現的需求,讓合併數據成為了不可能。兩次發送數據,是兩個線程完成的。但是有時候可能合併得到一起,有時候無法合併到一起,因為我還是想利用之前避開問題的方法來實現,結果效果很不理想。
所以,在最開始出現連續兩次發送數據造成死迴圈的時候,我將我上層的業務代碼全部幹掉,直接寫測試代碼,結果發現一樣會出現死迴圈。這樣之後,我就認定是Libuv的坑了。所以後來就只有饒坑了,心情很是不爽。
按照其他同事的說法,Libuv是nodejs使用的,應該不會出現這麼低級的問題吧。其實我也覺得不應該出現這麼低級的問題,然而這個問題就在這,我都不知道為什麼。只能先將鍋甩給了libuv。
當後面的需求,無法繞過去的時候,還是要直接面對這個坑的問題。怎麼辦呢?那就只能學習和深入研究源碼了。最要命的是,Libuv的資料太少了,就有那麼一本英文書,網路有人正在翻譯為中文,還沒有翻譯完。而且這個書也不是那麼全面,至少我讀了之後,還是沒有完全明白的意思,懵懵懂懂的。除了這個,基本上沒有比較深入的資料了。要麼是一點學習筆記,寫了一個demo,無關痛癢。
正是因為Libuv文檔太少了,讓學習Libuv變得困難,出現問題都無從查詢資料。而源碼,也不是誰都能夠看懂的。我也不願意去鑽研源碼,如果能夠解決問題,絕不研究源碼。如果是對源碼本身感興趣,那也是閑暇的時候研究,而工作比較緊張,沒有那麼多時間。
事情已經進行到了必須面對問題的時候,所以我就開始再從僅有的少數資料里和代碼里研究問題。然後也加了一個QQ群,群也就3個。對於示例代碼的一個簡單的問題,一個群友竟然說要500RMB才肯解答,說他研究了2年了。哎~ 我自己繼續研究好了。
然後不斷的調試代碼,分析代碼的流程,這次是直接使用libuv的測試代碼研究,通過熟悉代碼,測試,然後竟然成功的實現了連續兩次伺服器回覆命令。2017年6月3日上午,實現了一個版本。然後晚上,又實現了另外一個版本。第一個版本比較湊合,第二個版本最接近於我使用的版本,也就是前面提到的改成Linux下的libuv包裝類,其實和示例代碼差不多。在這個示例測試中,直接連續兩次發送數據,和項目中的問題一樣。然後通過不懈的努力,然後成功實現了連續兩次發送數據,依然正常運行。
到現在為止,我才真正明白我之前的問題所在了。問題不在libuv,也不在於網友phata的Windows版的libuv包裝類,而是在於我改成linux版的libuv包裝類。我忽略的一點就是,libuv在Windows上使用的是完成埠,而在linux上使用的是非同步事件epoll。兩者是不一樣的,所以在改成linux版libuv包裝類的時候,我沒有做好處理,只是簡單的改了一下,忽略了底層實現的差異,才出現了這個問題。所以在Windows上沒有問題,在Linux有問題。我竟然直接根據這個判定libuv有這樣一個低級的坑!可見我是多麼無知。
所以我總結一句話:完全相信權威,那是迷信;而不深入調查,僅根據表明現象就直接否定權威,那是無知!!
我們經常聽說不要完全相信權威,要敢於質疑權威,但是很多時候,我們卻不知道,質疑權威應該如何質疑。正確的質疑是用實踐去證明,用合理的證據證明權威是錯的。而我這個行為,是沒有經過深入的調查研究的,所以是無知的表現。還好,有這機會讓我直接面對問題,讓我查出問題的原因,讓我反省,讓我矯正自己的態度。
寫下這篇文章,記錄一下此時我的無知,在今後的研究路上,多一份謙遜,多一份研究,少一點無知,也用以警告我自己,不要隨意下定論。
註:Linux版本的libuv包裝類,你還是可以使用的,只是不要對一個請求回覆多個命令。一對一的回覆,這個包裝類是可以用的。後面我再想辦法改進,修複這個問題。所以分享的那個代碼是可以用,只是要註意這個問題。