Actors 基于消息驱动的异步编程模型
Actors 是一種異步編程模式,它的提出主要是為了解決 “多線程,MTA” 編程帶來(lái)的復(fù)雜性、困難度。
但這些都不是 Actors 本身提供的最大優(yōu)點(diǎn),它真正的價(jià)值是易于解決多線程處理,容易導(dǎo)致共享資源(share resources)之間產(chǎn)生競(jìng)爭(zhēng),故而導(dǎo)致 deadlock(死鎖)。
一個(gè)軟件工程項(xiàng)目里面并非所有的開(kāi)發(fā)人員都經(jīng)歷過(guò)嚴(yán)格的?“多核編程” 數(shù)年摧殘,縱然受過(guò)多年毒打的開(kāi)發(fā)人員有時(shí)也容易寫(xiě)出 deadlock(死鎖)的疑難問(wèn)題,往往經(jīng)驗(yàn)及技術(shù)越強(qiáng)的人搞出死鎖就越難解決。
設(shè)計(jì)模式及編程模型,提出目的都是為了讓代碼寫(xiě)起來(lái)更簡(jiǎn)單、更易于閱讀、后期工程維護(hù),但不意味著設(shè)計(jì)模式及編程模型的應(yīng)用會(huì)讓代碼執(zhí)行的效率更高,或許是更低也說(shuō)不一定。
有時(shí)候不要過(guò)于注重形式,沒(méi)有意義,可以解決人們迫切問(wèn)題的設(shè)計(jì)模式及編程模型才是有價(jià)值與意義的,個(gè)人多年經(jīng)驗(yàn)之談。
Actors 基于消息驅(qū)動(dòng)的異步編程模型,本文在標(biāo)題上就已經(jīng)明確提出了 Actors 異步編程模型的核心要點(diǎn):“基于消息驅(qū)動(dòng)”
每個(gè)線程可以運(yùn)行一個(gè)或多個(gè) Actors 消息驅(qū)動(dòng)器,有些類(lèi)似 Windows 窗體編程中的消息隊(duì)列(WndProc),當(dāng)某個(gè)消息到達(dá),人們對(duì)感興趣的消息逐個(gè)進(jìn)行處理。
Actors 異步編程模型里面要求,所有的線程或進(jìn)程之間都通過(guò)消息的方式進(jìn)行合作,所以采用 Actors 異步編程模型的結(jié)構(gòu)大約如下圖所示:
?上圖為簡(jiǎn)化的理解圖,Actors 是一個(gè)消息生產(chǎn) + 消費(fèi)的驅(qū)動(dòng)模型,設(shè)A與B兩個(gè)端點(diǎn),A為生產(chǎn)Actors Action,B為消費(fèi)處理 Actors Action 端點(diǎn)
A與B之間傳遞消息(Actor Action)的傳輸層介質(zhì),可以是進(jìn)程內(nèi)存、共享內(nèi)存、Socket套接字、Pipe 匿名/命名管道,Actor 本身不限制消息傳遞的介質(zhì)層。
Actors 模型本身不解決傳輸介質(zhì)層出現(xiàn)故障而導(dǎo)致的 Actor Actions 消息丟失的問(wèn)題,例如:A節(jié)點(diǎn)生產(chǎn)一個(gè)消息經(jīng)Socket套接字推送到B節(jié)點(diǎn),但Socket套接字發(fā)生故障導(dǎo)致該生產(chǎn)的消息被丟失。
如何解決此問(wèn)題?對(duì)于異步編程而言,一些行為并不需要ACK確認(rèn)已經(jīng)處理該消息,但如果確定需要對(duì)方處理該消息,則需要實(shí)現(xiàn)一種名為 “停等ARQ”【停等超時(shí)自動(dòng)重傳】ACK確認(rèn)的機(jī)制以確保消息被傳遞到目標(biāo)節(jié)點(diǎn)上被處理,但這屬于傳輸介質(zhì)層設(shè)計(jì)時(shí)需要考慮的東西,與 Actors 模型并沒(méi)有任何關(guān)系。
所以,人們從此處不需要參考 Actors 更多博客/書(shū)籍/文獻(xiàn)就可以自行推導(dǎo)出,Actors 從設(shè)計(jì)之初就是為了解決多線程編程的一些問(wèn)題,而不是為了分布式而設(shè)計(jì)的,只是發(fā)展到分布式的時(shí)代仍然有大量的解決方案工程采用 Actors 異步編程編程模型而已。
那么什么是本文提出的 “Actors Action”?
比如A節(jié)點(diǎn)向B節(jié)點(diǎn) Publish(生產(chǎn))一個(gè) Actor Action,那么它至少攜帶以下信息:
1、Action ID(動(dòng)作ID)
2、進(jìn)階:Sequence ID(序列ID)用于ACK確認(rèn),注意:它與 Actor 模型本身無(wú)關(guān)
3、Action 載荷的數(shù)據(jù)模型(貧血模型)
4、Origin(來(lái)源信息若不需要應(yīng)答則不需要)
B節(jié)點(diǎn)從消息隊(duì)列中 Peek 彈出頂部入列消息(FIFO先入先出原則),調(diào)度派發(fā)器則基于 Action ID 來(lái)派發(fā)消息到對(duì)應(yīng)的 Actor Action Handler(Actor 動(dòng)作處理器)來(lái)消費(fèi)處理該動(dòng)作的行為。
那么 Action 載荷的數(shù)據(jù)模型,如何在調(diào)度派發(fā)器內(nèi)解決呢?
很容易:
C# 工程語(yǔ)言可以編寫(xiě)工程內(nèi)消息模型的序列化算法,根據(jù)ID來(lái)映射對(duì)應(yīng)的 “貧血數(shù)據(jù)模型”,管理的方法有兩種:
1、基于反射檢索元數(shù)據(jù)的方式來(lái)自動(dòng)構(gòu)建及建立映射,例如在數(shù)據(jù)模型頭上聲明特性,靜態(tài)標(biāo)記:Action ID。
2、手動(dòng)注冊(cè) Action ID 跟數(shù)據(jù)模型的映射關(guān)系
C/C++ 工程語(yǔ)言可以編寫(xiě)工程內(nèi)消息模型的靜態(tài)序列化算法,如采用靜態(tài)編譯的序列化?google protobuf,解決方案仍是手動(dòng)注冊(cè) Action ID 跟數(shù)據(jù)模型之間的映射關(guān)系。
當(dāng)我們?yōu)?Action 增加進(jìn)階的序列ID,那么則可以實(shí)現(xiàn) “停等ARQ” 的機(jī)制,那么也為RPC遠(yuǎn)過(guò)程調(diào)用的實(shí)現(xiàn)提供可能性,如果我們?cè)?C/C++ 語(yǔ)言中,最好實(shí)現(xiàn)的編程模型為APM(異步編程模型)
例如:
修改玩家狀態(tài)
參考一:ChangePlayerStatusAsync( args..., lambda...?)
參考二:BeginChangePlayerStatus( args...., [...](args...) {
? ?auto result = EndChangePlayerStatus(...);
? ?// TO:DO Your are code here.
})
但當(dāng)我們?cè)?Actors 異步編程模型的基礎(chǔ)上,增加了RPC遠(yuǎn)過(guò)程調(diào)用,那么則可能帶來(lái)一個(gè)全新的潛在問(wèn)題,“deadlock” 邏輯層的死鎖,如:A遠(yuǎn)過(guò)程調(diào)用B,B遠(yuǎn)過(guò)程調(diào)用A,調(diào)用上下層級(jí)關(guān)系不明確,或許大多數(shù)開(kāi)發(fā)人員皆曾犯過(guò)該錯(cuò)誤。
注意:此類(lèi)錯(cuò)誤跟 Actors 異步編程模式并沒(méi)有直接關(guān)系,將其歸納于 Actors 異步編程模式本身的缺點(diǎn),理解上有問(wèn)題的。
Actors 異步編程模型總結(jié):
重點(diǎn)(一):
Actors 異步編程模式,本身是沒(méi)有多線程下的安全問(wèn)題的,原因很明顯,它是只是把消息生產(chǎn)并推送到消息消費(fèi)者的消息隊(duì)列,這個(gè)過(guò)程甚至不需要加鎖(臨界區(qū))
這取決于底層傳輸媒介,若同個(gè)進(jìn)程內(nèi)存?zhèn)鬟f的需要上鎖,消息隊(duì)列本身并非必是線程安全,例如:C/C++ 工程語(yǔ)言常常適用STL標(biāo)準(zhǔn)庫(kù)中的 std::queue、std::list 泛型模板BCL基礎(chǔ)類(lèi)庫(kù)。
重點(diǎn)(二):
Actors 異步編程模式,本身不提供停等ARQ,消息確認(rèn)機(jī)制,因?yàn)閷?shí)現(xiàn)類(lèi)似的機(jī)制導(dǎo)致的邏輯死鎖是開(kāi)發(fā)人員的問(wèn)題,不是 Actors 模式本身的缺點(diǎn),扣鍋還是的要點(diǎn)臉,沒(méi)有的東西不要給別人加頭上。
重點(diǎn)(三):
Actors 異步編程模式,是最初是用于解決并緩解 “單進(jìn)程多線程” 內(nèi)共享資源(share-resource)競(jìng)爭(zhēng)導(dǎo)致的死鎖、內(nèi)存安全、多核編程編碼復(fù)雜度的問(wèn)題。
例如:
A線程訪問(wèn)共享資源競(jìng)爭(zhēng)鎖,B線程訪問(wèn)共享資源競(jìng)爭(zhēng)到鎖,但A線程訪問(wèn)另外一個(gè)共享資源競(jìng)爭(zhēng)到鎖,B線程嘗試訪問(wèn)另外一個(gè)共享資源競(jìng)爭(zhēng)鎖,那么就會(huì)發(fā)生相互競(jìng)爭(zhēng)死鎖現(xiàn)象。
當(dāng)人采用:
Actors 模式時(shí),我們將該共享資源按需要自行劃分掛在到一個(gè)具體的 Actors 調(diào)度器負(fù)責(zé)驅(qū)動(dòng)處理,當(dāng)其它線程/進(jìn)程需要訪問(wèn)該共享資源時(shí),交付請(qǐng)求到目的?Actors 調(diào)度器上負(fù)責(zé)的?Actors Action Handler 進(jìn)行處理,那么操作該共享的資源都在單個(gè)工作線程上,所以不需要額外的增加互斥鎖。
Actors 模型系統(tǒng)執(zhí)行非常高效,但其效能表現(xiàn)仍無(wú)法比擬多線程內(nèi)存之間相互訪問(wèn)的效率,但并非絕對(duì)的,某些情況下 Actors 模型系統(tǒng)的效能表現(xiàn)優(yōu)于多線程模型的系統(tǒng),當(dāng)然不說(shuō)大家也明白不是,這么對(duì)比沒(méi)有意義。
重點(diǎn)(四):
Actors 異步編程模式,基于消息驅(qū)動(dòng),勢(shì)必會(huì)帶來(lái)大量碎片化的消息報(bào)文數(shù)據(jù),這必定造成嚴(yán)重內(nèi)存碎片問(wèn)題,致內(nèi)存分配的效率下跌,C/C++ 開(kāi)發(fā)人員應(yīng)注意對(duì)內(nèi)存碎片問(wèn)題進(jìn)行優(yōu)化處理,由更先進(jìn)的 “現(xiàn)代工業(yè)高級(jí)編程語(yǔ)言” 特性來(lái)解決,個(gè)人建議人們適用:“Microsoft C# by dotNET.”
C/C++ 可以采用以下幾類(lèi)解決方案:
1、定長(zhǎng)/對(duì)齊數(shù)據(jù)報(bào)文,盡量避免隨機(jī)顆粒化分配
2、分配固定大小區(qū)塊,劃分若干個(gè)小區(qū)塊,每個(gè)小區(qū)塊緩存一個(gè)消息
3、自行實(shí)現(xiàn)碎片整理(移動(dòng)內(nèi)存)
4、適用開(kāi)源內(nèi)存池,掩耳盜鈴把問(wèn)題丟向內(nèi)存池(例:jemalloc)
重點(diǎn)(五):
Actors 異步編程模式驅(qū)動(dòng)的執(zhí)行單元是一個(gè)線程(線程是程序執(zhí)行最小單元),如果單個(gè)線程負(fù)載過(guò)多性能會(huì)成為瓶頸的,但這并不算 Actors 模式本身的缺點(diǎn),如果需要承擔(dān)的負(fù)載過(guò)重,開(kāi)發(fā)人員應(yīng)當(dāng)思考如何優(yōu)化并解決
例如:更加細(xì)化的拆分非本職業(yè)務(wù)到其它的 Actors 的驅(qū)動(dòng)器/線程進(jìn)行負(fù)責(zé),而不是說(shuō)這就是 Actors 模式本身的缺點(diǎn),這不是一個(gè)好的解決問(wèn)題的態(tài)度。
如果學(xué)過(guò) “領(lǐng)域驅(qū)動(dòng)模型” 的童靴們,應(yīng)該很明白大多數(shù)某個(gè)具體 Actors 產(chǎn)生性能瓶頸,均是上層模型領(lǐng)域職責(zé)過(guò)于寬泛,負(fù)責(zé)的事務(wù)遠(yuǎn)遠(yuǎn)超出它本身應(yīng)當(dāng)承擔(dān)的事務(wù)導(dǎo)致的,但凡是不是絕對(duì)的,對(duì)多數(shù)軟件工程項(xiàng)目而言,它卻是問(wèn)題的本質(zhì)原因。
是的為每個(gè)模塊/接口的粒度劃分存在一些問(wèn)題,就像人們?cè)谶M(jìn)行多線程(多核)編程時(shí),需要注重鎖(lock)的粒度問(wèn)題,如果人們粒度太大那么多線程系統(tǒng)的效率可能還不如單線程的效能,如果粒度太小,編程復(fù)雜性就會(huì)直線提高,因?yàn)橥ǔfi粒度應(yīng)用越小越容易遇到死鎖的問(wèn)題,但相對(duì)的代碼效能通常就越高(指綜合并發(fā)效能指數(shù),對(duì)硬件負(fù)載及利用率越高)。
總結(jié)
以上是生活随笔為你收集整理的Actors 基于消息驱动的异步编程模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ShowWindow 显示窗口
- 下一篇: springboot项目部署后项目启动慢