對完成端口的一些看法

完成端口的主要好處在哪裏?
  完成端口的最大好處在於其管理海量連接時的處理效率,通過操作系統內核的相關機制完成IO處理的高效率。注意:完成端口的好處在於管理連接量的巨大,而不是傳輸數據量的巨大。在這種場合最適合用完成端口:連接量巨大,且每個連接上收發的數據包容易比較小,通常只有幾K甚至不到1K的字節。
  既然完成端口處理的是海量連接問題,那麼我們對完成端口的優化則也應該首先放在海量連接的相關管理上。爲此,我們引入“池”的概念。
  在完成端口的設計中,“池”幾乎是必須採用的原則。這裏的“池”包含有多個方面,分爲:線程池,內存池,連接池等等。下面,將逐一介紹這些“池”的含義及用途,稍後的文章裏,如果可能的話,我會給出採用了“池”和未採用“池”兩種情況下的效率對比,你將會發現“池”是多麼地可愛。
  在大型在線系統中,數據空間的頻繁創建和釋放是相當佔用系統資源的。爲此,在數據空間的管理上,我們引入了“內存池”。
  大家知道,在每一次的wsasend和wsarecv中,我們都要投遞一個結構體變量。這個結構體變量的創建和釋放,能有多種方式。
  方式一:即用即創建,即每次執行wsasend和wsarecv時都聲明一個新的結構體空間,GET完成後在工作者線程中銷燬;
  方式二:只有每出現一個新的連接時,我們才隨新連接建立一個新的結構體空間,將他和新的客戶端socket綁定在一起,只有當客戶端socket關閉時纔將他和客戶端對象一起銷燬;
  方式三:建立一定量的結構體空間,並將其統一放入一個空閒隊列,不管何時執行wsasend和wsarecv,都先從空閒隊列裏取一個結構體空間來用,用完後,將其放回空閒隊列。
  從執行的效率及實現的便捷性方面考慮,建議採用方式3的形式管理這些空閒結構體空間。採用方式3的難點在於,到底創建多少個結構體空間纔是比較合適的?這可能取決於你在自己服務器上作的完成端口的處理效率,如果完成端口的處理效率比較高,那麼需要的隊列長度可能就比較小,如果完成端口的處理效率低,那麼隊列長度就要大一點。
  下面說說“連接池”。我們知道,使用傳統的accept接收客戶端的一個連接後,此函數會返回一個創建成功的客戶端socket,也就是說,此函數自己完成了socket的創建工作。非常好的是,windows給我們提供了另一個函數,允許我們在接受連接之前就事先創建一個socket,使之於接受的連接相關聯,這個函數就是acceptEx,在acceptEx的參數中,SOCKET類型的參數值是我們事先用socket函數創建完成的一個socket,在調用acceptEx時,把這個已創建好的socket傳給acceptEx。說到這裏,明白的人可能已偷笑了--既然這樣,那豈不是允許我們在接受連接之前就創建好一大堆的socket等着acceptEx來用嗎?這就省去了臨時創建一個socket的系統開銷呀?呵呵,事實確實如此。在實際操作中,我們能事先創建好一大堆的socket,然後接受客戶端連接的函數使用acceptEx。這一大堆的socket,我們能形象地稱之爲“socket連接池”(當然,也許在更多的場合,我們聽到的是“數據庫連接池”的概念),這個名字是我自己取的,聽着覺得不舒服的兄弟,乾脆就叫他“socket池”吧,哈哈。
  線程池的概念,相信作過多線程的朋友都會有所耳聞。在此處的文字中,就不對他再多加介紹了,感興趣的朋友能自己去GOOGLE一下。我這裏說的線程池,不僅僅指由完成端口自己負責維護的那個工作者線程池,還指我們建立在完成端口模型基礎之上的線程池。這個線程池裏又具體分爲執行邏輯線程池和發送線程池。當然,也有人建議執行邏輯線程只要一條就行了,沒必要用多條,而且同步會非常麻煩。我贊同這種說法以,但前提是,這樣的設計,需求你對服務器功能的劃分要相當合理,否則會讓這一條線程累得夠嗆。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章