題目是一句golang編程箴言,對它的理解可大可小。
往小了說,golang建議使用channel來共享信息而不是使用共享內(nèi)存,這是一種優(yōu)雅的方式,避免了數(shù)據(jù)同步帶來的繁瑣和低效。
往大了說,本質(zhì)上還是讓資源去調(diào)度請求,而不是讓請求去調(diào)度資源。
有些時候,思維的轉(zhuǎn)變,問題的視角,會帶來意想不到的收獲
資源就那么多,所有請求有序使用資源的方式就是通信的方式,反過來,為每個請求虛擬出它獨占資源的假象,那就是共享的方式。兩種截然不同的方式,差異體現(xiàn)在仲裁成本,這個成本決定了它們承載并發(fā)的能力。
一個一個說。
電路交換 vs 分組交換
電路交換試圖占有整條電路(其實是最后一公里),若不成功,必須等到成功。
分組交換將長信息分割成若干小數(shù)據(jù)包,小數(shù)據(jù)包統(tǒng)計復用鏈路。
批處理系統(tǒng) vs 分時系統(tǒng)
批處理用戶一旦使用系統(tǒng),則會獨占系統(tǒng)到任務完成,其它用戶等待。
分時系統(tǒng)將時間分片,多用戶被調(diào)度復用時間片。
CSMA/CD vs 交換式以太網(wǎng)
CSMA/CD主機試圖獨占總線發(fā)送數(shù)據(jù)包,若不成功便退避直到成功。
交換式以太網(wǎng)數(shù)據(jù)包在交換機有序排隊,復用buffer。
Apache vs Nginx
Apache為每一個請求生成一個task,該task一旦獲得CPU,其它task將等待。
Nginx采用異步模型,所有請求分時復用固定數(shù)量task的CPU時間。
共享內(nèi)存 vs erlang/go channel
共享內(nèi)存對寫寫以及讀寫是互斥,每次只允許一個操作,其它不得不等待,重試。
erlang/go channel將內(nèi)容拆解為事務消息,依靠消息的有序傳遞共享信息。
...
我們來看上述兩兩比較的共性。
可將上述所有的二者抽象為爭搶模式和有序模式:
對于爭搶模式,本質(zhì)上需要對沖突進行仲裁。
對于有序模式,本質(zhì)上需要對并發(fā)進行調(diào)度。
所謂對沖突進行仲裁,意思就是發(fā)生沖突后怎么辦。無論是退避重試,還是等待,此期間均是什么都做不了,且仲裁本身需要昂貴的成本。
并發(fā)調(diào)度就會好太多,有序化便無沖突,也就沒有仲裁成本了,沒有了仲裁,也就無需重試,等待,便可以干別的了,處理完全異步化。
我們再來對比之前技術(shù)優(yōu)劣:
電路交換 vs 分組交換
電路交換一旦占線,你需要自己不斷重試。
分組交換你只管發(fā)數(shù)據(jù)包,交換節(jié)點會自動調(diào)度這些數(shù)據(jù)包到達目的地后重組。
批處理系統(tǒng) vs 分時系統(tǒng)
批處理系統(tǒng)一旦系統(tǒng)被占,你就要排隊等待或者待會兒再來。
分時系統(tǒng)你只需要下發(fā)任務,任務調(diào)度系統(tǒng)會讓所有用戶的任務分時復用時間片。
CSMA/CD vs 交換式以太網(wǎng)
CSMA/CD網(wǎng)卡需要不斷監(jiān)聽沖突并重試。
交換式以太網(wǎng)卡只需要發(fā)包,交換機會排隊調(diào)度來不及轉(zhuǎn)發(fā)的數(shù)據(jù)包。
Apache vs Nginx
Apache線程/進程若沒被調(diào)度到CPU,就需要等待直到被調(diào)度切換至CPU。
Nginx只需將事件通知到,工作進程便會輪詢處理完所有請求。
共享內(nèi)存 vs erlang/go channel
共享內(nèi)存訪問需要加鎖,若持鎖失敗,要么忙等重試,要么待會兒再來。
erlang/go channel以消息傳遞通信,消息發(fā)出后就不用管了,除非它希望得到回饋,完全異步。
可見,這又是一個殊途同歸。同類的還有:
PCI vs PCIe,從總線到交換。
宏內(nèi)核 vs 微內(nèi)核,從共享數(shù)據(jù)結(jié)構(gòu)到消息傳遞。
Spin/RW Lock vs RCU Lock,從爭搶鎖到操作副本原子更新。
RCU原理
為什么沖突仲裁的爭搶模式無法承載大并發(fā),因為過載的沖突仲裁開銷會將資源淹沒,若要承載大并發(fā),必然要采用調(diào)度的方式。要理解這一要素,需要換一個視角。
我們看操作的是信息的本身,還是信息的副本。
回到本文題目,“以通信方式共享內(nèi)存”操作信息的副本, 而“以共享內(nèi)存方式通信”則操作信息本身。
操作信息副本可以保證同時有且只有一個實體操作該副本,如果有兩個實體需要操作該副本,那就再復制一個副本,這就保證了無沖突,業(yè)務流是可控無阻塞的。
RCU可做到業(yè)務無阻塞并發(fā),無論是spinlock還是rwlock,都做不到。spinlock/rwlock鎖臨界區(qū),造成臨界區(qū)串行化,而RCU沒臨界區(qū),它將本屬于臨界區(qū)的邏輯作為副本操作,擇機原子更新,這便可做到無阻塞并發(fā)。
操作副本是無阻塞并發(fā)的甘泉,如果把并發(fā)看作是時間擴展性,那么將信息共享到遠方則是空間擴展性,完成這件事的是網(wǎng)絡,目前它是TCP/IP網(wǎng)絡。TCP/IP網(wǎng)絡采用了“以通信方式共享內(nèi)存”的方式,它無疑是正確的。
我不懂erlang,但大致知道它的意思,erlang沒有變量,只操作副本,它是通信網(wǎng)絡在編程語言上的映射,對于golang,大概也是如此,使用go channel可以像網(wǎng)絡收發(fā)一樣來處理信息。
我們看socket接口,它實屬用通信的方式共享內(nèi)存的古老方式。
socket接口一開始是進程間通信機制,與之通信的進程可在本機,也可在遠處,可在世界任意地方。“以通信方式共享內(nèi)存“,是最原始的編程模式,一直到現(xiàn)在依然正確。
共享內(nèi)存是一種本地優(yōu)化,僅有編程意義,卻沒有擴展性,無論是無阻塞并發(fā)的時間擴展性,還是將信息傳遞給遠方的空間擴展性。
共享內(nèi)存是一種本地優(yōu)化,優(yōu)化的是指令操作延時,與其將信息封裝成消息并傳遞,不如直接操作信息本身,它編程更簡單,代碼指令更少,執(zhí)行延時更低。但高并發(fā)并不care指令延時,高并發(fā)care同時執(zhí)行的有效指令數(shù),而spin,switch不屬于有效指令,故共享內(nèi)存天生不與高并發(fā)配對。
此外,還是那個觀點,網(wǎng)絡編程場景,普遍毫秒級的單流通信延時,共享內(nèi)存相比消息傳遞節(jié)省個微妙甚至納秒級的操作延時,并無太大意義。要怪就怪光速吧。
從云原生開始
云原生是面向微服務的架構(gòu),而消息傳遞是微服務交互的媒介,每個工人都接觸過關(guān)于消息隊列的概念,正是消息支撐了云原生微服務。
消息并不封裝狀態(tài),消息本身無狀態(tài),狀態(tài)通過消息之間的交互來體現(xiàn)。消息交互可自由組合,這是分布式的源泉,而云原生本身就是面向分布式的設(shè)計。
一個部署云原生應用的IDC機房就是縮小版的全球TCP/IP互聯(lián)網(wǎng),無狀態(tài)的消息在分布式的微服務之間傳遞,狀態(tài)僅由微服務的交互定義和維護。
甚至一臺物理主機內(nèi)部板卡也成了微型版的全球TCP/IP互聯(lián)網(wǎng),無狀態(tài)的消息在分布式的模塊之間傳遞,狀態(tài)僅由模塊之間的交互定義和維護。
共享內(nèi)存類似總線,大家擁有平等訪問權(quán),但寫訪問時要獨占。我們可以從總線和消息交換的關(guān)系看共享內(nèi)存的處境。
曾經(jīng),主機主板上很多總線,很多模塊都要先爭搶獲得總線控制權(quán)才能與CPU或別的模塊通信,但后來PCIe將總線改成了由Hub互聯(lián)的交換網(wǎng)絡,采用消息交換替換了總線仲裁。
以太網(wǎng)在此之前已經(jīng)走過了同樣的軌跡。
近來年被工人們提倡的微內(nèi)核思想,大致也是這么回事,將對共享數(shù)據(jù)結(jié)構(gòu)的操作換成了消息傳遞。
為什么這些都和全球TCP/IP互聯(lián)網(wǎng)類比呢?因為TCP/IP的基礎(chǔ)就是異步的,無狀態(tài)的,分布式的,消息傳遞的分組交換網(wǎng)。
總線簡單樸素,隨著系統(tǒng)規(guī)模的擴大,總線爭搶帶來的時間損耗指數(shù)級上升,人們發(fā)現(xiàn)總線無法支持高并發(fā)及無法物理擴展時,消息傳遞便替換了總線。大規(guī)模系統(tǒng),消息操作帶來的額外延時是可以忽略不計的。
無論內(nèi)部板卡,局域網(wǎng),PCI,操作系統(tǒng)都是從局域范圍開始的,它們一開始從總線開始便不足為奇。然而互聯(lián)網(wǎng)一開始就是連接分布式廣域端的,一開始就不適合采用總線結(jié)構(gòu),這反過來說明總線在分布式場景的不適用。
我一向贊美TCP/IP端到端原則,正是它無狀態(tài)的IP細腰讓互聯(lián)網(wǎng)規(guī)模得以任意擴大而不引入額外開銷,而細腰也是無狀態(tài)消息交換的核心,只在發(fā)送端和接收端之間定義和維護狀態(tài),而不是所有端一起維護共享總線或內(nèi)存的狀態(tài)。
因此,消息傳遞也遵循端到端原則,可以自由擴展規(guī)模,總線和共享內(nèi)存則相反。
以上從局域擴而大之的視角,我們看到了消息傳遞替換總線的趨勢。
反過來,從廣域向內(nèi)縮,規(guī)模在漸小,傳輸延時在漸短,越來越不分布式,無狀態(tài)消息傳遞帶來的可擴展優(yōu)勢越發(fā)無用武之地,其額外封裝帶來的額外延時逐漸承擔了端到端延時的大頭。
除去額外的消息封裝和傳輸操作,所有不多的實體直接操作信息所在的內(nèi)存,最小化端到端延時便成了可觀的收益,因此,總線和共享內(nèi)存便是微縮版系統(tǒng)的極致了。
總結(jié)
這就兩邊都說得通了,從小規(guī)模到大規(guī)模,總線和共享內(nèi)存被消息傳遞替代,從大規(guī)模到小規(guī)模,總線和共享內(nèi)存則是消息傳遞的優(yōu)化。
就像廣義相對論,牛頓力學,量子力學一樣,不同的規(guī)模尺度有不同的哲學。
-
以太網(wǎng)
+關(guān)注
關(guān)注
40文章
5460瀏覽量
172736 -
內(nèi)存
+關(guān)注
關(guān)注
8文章
3055瀏覽量
74331 -
總線
+關(guān)注
關(guān)注
10文章
2903瀏覽量
88396
原文標題:深刻理解 | 以通信方式共享內(nèi)存,不要以共享內(nèi)存方式通信
文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
內(nèi)存共享原理解析
![<b class='flag-5'>內(nèi)存</b><b class='flag-5'>共享</b>原理解析](https://file1.elecfans.com/web2/M00/C0/29/wKgZomXS_W-Ae4zJAAIGPyOH6wY800.jpg)
關(guān)于共享內(nèi)存的函數(shù)shmget()
linux如何共享內(nèi)存實驗
linux中的共享內(nèi)存是指什么?共享內(nèi)存有哪些優(yōu)缺點
理解并使用共享內(nèi)存
基于共享內(nèi)存多核數(shù)據(jù)結(jié)構(gòu)研究
共享內(nèi)存IPC原理,Linux進程間如何共享內(nèi)存?
![<b class='flag-5'>共享</b><b class='flag-5'>內(nèi)存</b>IPC原理,Linux進程間如何<b class='flag-5'>共享</b><b class='flag-5'>內(nèi)存</b>?](https://file.elecfans.com/web1/M00/57/C8/pIYBAFtMMSeAJuF1AAAZquF3f4s114.png)
深入剖析Linux共享內(nèi)存原理
![深入剖析Linux<b class='flag-5'>共享</b><b class='flag-5'>內(nèi)存</b>原理](https://file.elecfans.com/web2/M00/1A/95/pYYBAGF8pd2AYuFZAAAPvlXKe1I884.png)
評論