想象一下當初為什麼不讓多個進程/線程在一個相同的IP地址和端口上偵聽,很簡單,這是因為TCP/IP模型將一個端口作為一個四層復用解復用的唯一標識,也就是一個四層地址,正如IP地址屬於一個主機一樣(屬於一塊網卡?),一個IP/端口對屬於一台主機上一個特定的進程,它只是一個保證唯一性的靜態標識。世界上不同的主機不能有相同的IP地址,一台主機上綁定特定IP地址的不同進程也不能有相同的端口,否則就不知道一個流到底該交給哪個進程!
想象一下現在為什麼reuseport可以讓以前不可能的事變成可能。很簡單,在靜態因素之外加入了一個動態因素,那就是將發起連接的源IP和源端口也一起考慮了進來,四元組一起做了一個簡單的hash計算,所得的結果對Listener數量取模,獲取哪個Listener要為這個連接服務。
事實上,我們發現,需要唯一標識的不是一個Listener,而應該是一個連接本身。TCP服務端在有客戶端企圖建立一個連接時才有意義。那麼是什麼讓一個綁定同一IP/端口的套接字只能Listen一次這麼一個限制存在了這麼久呢?我認為答案有兩個方面,一方面是因為UNIX的進程模型,另一方面是這個限制在單核CPU時代工作的足夠好,又可以避免很多問題。
好吧,現在我將Listener和進程完全分割開,我既不贊同綁定同一IP地址/端口的套接字只能Listen一次,又不贊同采用reuseport方案,我暫且忽略了哪個進程/線程在偵聽,假設根本沒有任何進程/線程偵聽的概念,我只求一個連接請求到來的時候,可以成功完成三次握手,創建一個客戶socket,而這個很簡單,新創建的客戶socket被放入一個池中,Listen的任務就完成了,在握手完成之前,與任何進程/線程都無關聯,接下來把進程/線程考慮進來,它們來accept,也就是從一個池中獲取一個客戶socket來處理。事實上,我是分離了Listen和Accept,內核協議棧只負責Listen,而進程/線程只負責Accept,問題就解決了。
雜亂不清的東西糾纏在一起的時候,會引入很多復雜性,避免這些復雜性的方式就是把糾纏在一起的東西剝離,海闊天空。同事為我這個優化取了一個很好的名字,叫做Xsocket,這裡的X可以理解成兩個意思,一個是“牛X”中的X,一個是“插”!!