Akka入门(一)Akka简介与为什么使用Akka
?AKKA是什么
AKKA是一套開源庫,用于設(shè)計(jì)跨處理器和跨網(wǎng)絡(luò)的可擴(kuò)展彈性系統(tǒng)。Akka允許開發(fā)者專注于滿足業(yè)務(wù)需求,而不是編寫低級(jí)代碼以提供可靠的行為,容錯(cuò)和高性能。
分布式系統(tǒng)環(huán)境中,組件崩潰而不響應(yīng),消息丟失而沒有線路上的跟蹤,并且網(wǎng)絡(luò)延遲會(huì)發(fā)生波動(dòng)。這些問題經(jīng)常發(fā)生在精心管?理的數(shù)據(jù)中心內(nèi)部環(huán)境中 - 在虛擬化體系結(jié)構(gòu)中更是如此。
為了幫助您處理這些現(xiàn)實(shí)問題,Akka提供:
- 多線程行為,不使用原子或鎖等低級(jí)并發(fā)結(jié)構(gòu) - 使您甚至不必考慮內(nèi)存可見性問題。
- 系統(tǒng)及其組件之間的透明遠(yuǎn)程通信 - 使您免于編寫和維護(hù)困難的網(wǎng)絡(luò)代碼。
- 集群,高可用性架構(gòu),具有彈性,可按需擴(kuò)展或擴(kuò)展 - 使您能夠提供真正的響應(yīng)式系統(tǒng)。
為什么使用Akka(為什么現(xiàn)代系統(tǒng)需要一個(gè)新的編程模型)
(1)封裝的挑戰(zhàn)
OOP的核心支柱是封裝。封裝規(guī)定對(duì)象的內(nèi)部數(shù)據(jù)不能直接從外部訪問;?它只能通過調(diào)用一組策劃方法來修改。該對(duì)象負(fù)責(zé)公開保護(hù)其封裝數(shù)據(jù)的不變性的安全操作。
當(dāng)我們分析OOP運(yùn)行時(shí)行為時(shí),我們有時(shí)會(huì)繪制一個(gè)消息序列圖,顯示方法調(diào)用的交互。例如:
不幸的是,上圖不能準(zhǔn)確地表示執(zhí)行期間實(shí)例的生命線。實(shí)際上,線程執(zhí)行所有這些調(diào)用,并且不變量的強(qiáng)制發(fā)生在調(diào)用該方法的同一線程上。使用執(zhí)行線程更新圖表,它看起來像這樣:
?
但當(dāng)多線程的情況下,我們整齊地繪制的圖表變得不充分。有的時(shí)候會(huì)有多個(gè)線程訪問同一個(gè)實(shí)例。
?
有一個(gè)執(zhí)行部分,其中兩個(gè)線程進(jìn)入相同的方法。不幸的是,對(duì)象的封裝模型并不保證該部分中發(fā)生的事情。兩個(gè)調(diào)用的指令可以以任意方式交錯(cuò)。這種情況會(huì)在多線程的情況下更加復(fù)雜。
解決此問題的常用方法是圍繞這些方法添加鎖定。雖然這確保了在任何給定時(shí)間最多只有一個(gè)線程將進(jìn)入該方法,但這是一個(gè)非常昂貴的策略:
- 鎖嚴(yán)重限制了并發(fā)性,它們?cè)诂F(xiàn)代CPU架構(gòu)上非常昂貴,需要從操作系統(tǒng)中重載以暫停線程并在以后恢復(fù)它。
- 調(diào)用者線程現(xiàn)在被阻止,因此它無法執(zhí)行任何其他有意義的工作。即使在桌面應(yīng)用程序中這也是不可接受的,我們希望即使在長(zhǎng)后臺(tái)作業(yè)運(yùn)行時(shí),也要保持面向用戶的應(yīng)用程序部分(其UI)能夠響應(yīng)。在后端,阻塞是徹頭徹尾的浪費(fèi)。有人可能認(rèn)為這可以通過啟動(dòng)新線程來補(bǔ)償,但線程也是一種代價(jià)高昂的抽象。
- Locks引入了一個(gè)新的威脅:死鎖。?
這些現(xiàn)實(shí)導(dǎo)致了一個(gè)局面:
?
此外,鎖只能在本地很好地工作。在協(xié)調(diào)跨多臺(tái)機(jī)器時(shí),唯一的選擇是分布式鎖。不幸的是,分布式鎖定的效率比本地鎖定效率低幾個(gè)級(jí)別,并且通常會(huì)在擴(kuò)展時(shí)施加限制。分布式鎖定協(xié)議需要在多臺(tái)計(jì)算機(jī)上通過網(wǎng)絡(luò)進(jìn)行多次通信往返,因此造成大量延遲。
在面向?qū)ο笳Z言中,我們很少考慮線程或線性執(zhí)行路徑。我們經(jīng)常將系統(tǒng)設(shè)想為對(duì)象實(shí)例的網(wǎng)絡(luò),它們對(duì)方法調(diào)用作出反應(yīng),修改其內(nèi)部狀態(tài),然后通過驅(qū)動(dòng)整個(gè)應(yīng)用程序狀態(tài)的方法調(diào)用相互通信:
但是,在多線程分布式環(huán)境中,實(shí)際發(fā)生的是線程通過以下方法調(diào)用“遍歷”此對(duì)象實(shí)例網(wǎng)絡(luò)。
?
?
- 沒有足夠的鎖,狀態(tài)就會(huì)被破壞。
- 由于存在許多鎖定,性能會(huì)受到影響并且很容易導(dǎo)致死鎖。
總結(jié):
?
?
(2)?現(xiàn)代計(jì)算機(jī)體系結(jié)構(gòu)中共享內(nèi)存的錯(cuò)覺
- 對(duì)象只能保證面對(duì)單線程訪問時(shí)的封裝(保護(hù)不變量),多線程執(zhí)行幾乎總會(huì)導(dǎo)致內(nèi)部狀態(tài)損壞。
- 雖然鎖似乎是支持多線程封裝的自然補(bǔ)救措施,但實(shí)際上它們效率低,并且在任何實(shí)際規(guī)模的應(yīng)用中都容易導(dǎo)致死鎖。
- 鎖在本地工作,嘗試使它們分布存在,但提供擴(kuò)展?jié)摿κ钟邢蕖?/strong>
在現(xiàn)代架構(gòu)上 - 如果我們稍微簡(jiǎn)化一下 - CPU正在寫入緩存,而不是直接寫入內(nèi)存。這些高速緩存中的大多數(shù)都是CPU內(nèi)核的本地高速緩存,也就是說,一個(gè)內(nèi)核的寫入不會(huì)被另一個(gè)內(nèi)核看到。為了使本地更改對(duì)另一個(gè)核心可見,從而對(duì)另一個(gè)線程可見,需要將緩存行傳送到另一個(gè)核心的緩存。
在JVM上,我們必須使用volatile標(biāo)記或Atomic包裝器明確表示要跨線程共享的內(nèi)存位置。否則,我們只能在鎖定的部分訪問它們。為什么我們不將所有變量標(biāo)記為volatile?因?yàn)榭绾诵倪\(yùn)送緩存行是一項(xiàng)非常昂貴的操作!這樣做會(huì)隱含地使所涉及的核心停止執(zhí)行額外的工作,并導(dǎo)致高速緩存一致性協(xié)議(協(xié)議CPU用于在主存儲(chǔ)器和其他CPU之間傳輸高速緩存線)的瓶頸。結(jié)果是放緩的幅度。
總結(jié):
?
?
(3)調(diào)用堆棧的錯(cuò)覺
當(dāng)線程打算將任務(wù)委托給“后臺(tái)”時(shí)會(huì)出現(xiàn)問題。實(shí)際上,這實(shí)際上意味著委托給另一個(gè)線程。通常發(fā)生的是,“調(diào)用者”將一個(gè)對(duì)象放入由工作線程(“被調(diào)用者”)共享的內(nèi)存位置,然后工作線程在某個(gè)事件循環(huán)中將其拾取。這允許“調(diào)用者”線程繼續(xù)前進(jìn)并執(zhí)行其他任務(wù)。
問題是:如何通知“調(diào)用者”完成任務(wù)?但是當(dāng)任務(wù)因異常而失敗時(shí)會(huì)出現(xiàn)更嚴(yán)重的問題。異常傳播到哪里?它將傳播到工作線程的異常處理程序,完全忽略了實(shí)際的“調(diào)用者”是誰:
?
?
- 沒有真正的共享內(nèi)存,CPU核心就像網(wǎng)絡(luò)上的計(jì)算機(jī)一樣明確地將數(shù)據(jù)塊(高速緩存行)傳遞給彼此。CPU間通信和網(wǎng)絡(luò)通信比許多人意識(shí)到的更為共同。現(xiàn)在可以通過CPU或聯(lián)網(wǎng)計(jì)算機(jī)傳遞消息。
- 不是通過標(biāo)記為共享或使用原子數(shù)據(jù)結(jié)構(gòu)的變量隱藏消息傳遞方面,而是更有紀(jì)律和原則性的方法是將狀態(tài)本地保持為并發(fā)實(shí)體,并通過消息顯式地在并發(fā)實(shí)體之間傳播數(shù)據(jù)或事件。
這是一個(gè)嚴(yán)重的問題。工作線程如何處理這種情況?它可能無法解決問題,因?yàn)樗ǔo視任務(wù)失敗的目的。需要以某種方式通知“調(diào)用者”線程,但沒有調(diào)用堆棧可以解除異常。失敗通知只能通過旁道進(jìn)行,例如將錯(cuò)誤代碼置于“調(diào)用者”線程否則預(yù)期結(jié)果準(zhǔn)備好的位置。如果沒有這個(gè)通知,“調(diào)用者”永遠(yuǎn)不會(huì)收到失敗通知,任務(wù)就會(huì)丟失!這與網(wǎng)絡(luò)系統(tǒng)如何在沒有任何通知的情況下丟失/失敗消息/請(qǐng)求的情況類似。
當(dāng)事情出現(xiàn)問題時(shí),這種糟糕的情況會(huì)變得更糟,并且由線程支持的工作人員遇到錯(cuò)誤并最終處于無法恢復(fù)的狀態(tài)。例如,由bug引起的內(nèi)部異常會(huì)冒泡到線程的根目錄并使線程關(guān)閉。這立即引發(fā)了一個(gè)問題,誰應(yīng)該重新啟動(dòng)線程托管的服務(wù)的正常操作,以及如何將其恢復(fù)到已知良好的狀態(tài)?乍一看,這似乎是可以管理的,但我們突然面臨一個(gè)新的意外現(xiàn)象:線程當(dāng)前正在處理的實(shí)際任務(wù)不再位于從中獲取任務(wù)的共享內(nèi)存位置(通常是隊(duì)列) )。事實(shí)上,由于異常到達(dá)頂部,展開所有調(diào)用堆棧,任務(wù)狀態(tài)完全丟失!我們已經(jīng)丟失了一條消息,即使這是本地通信而沒有涉及網(wǎng)絡(luò)(消息丟失是預(yù)期的)。
綜上所述:
?
本文翻譯自akka官方文檔,并添加了部分個(gè)人理解如有需要請(qǐng)參考??https://akka.io/docs/
?
?
- 為了在當(dāng)前系統(tǒng)上實(shí)現(xiàn)任何有意義的并發(fā)性和性能,線程必須以有效的方式在彼此之間委派任務(wù)而不會(huì)阻塞。使用這種任務(wù)委托并發(fā)方式(對(duì)于網(wǎng)絡(luò)/分布式計(jì)算更是如此),基于調(diào)用堆棧的錯(cuò)誤處理會(huì)中斷,并且需要引入新的,明確的錯(cuò)誤信令機(jī)制。失敗成為域模型的一部分。
- 具有工作委托的并發(fā)系統(tǒng)需要處理服務(wù)故障并具有從中恢復(fù)的原則性方法。此類服務(wù)的客戶端需要知道任務(wù)/消息可能在重新啟動(dòng)期間丟失。即使沒有發(fā)生丟失,由于先前排隊(duì)的任務(wù)(長(zhǎng)隊(duì)列),垃圾收集造成的延遲等,響應(yīng)可能會(huì)被任意延遲。面對(duì)這些,并發(fā)系統(tǒng)應(yīng)該以超時(shí)的形式處理響應(yīng)期限?,像網(wǎng)絡(luò)/分布式系統(tǒng)。?
總結(jié)
以上是生活随笔為你收集整理的Akka入门(一)Akka简介与为什么使用Akka的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu18.04安装JDK1.8和
- 下一篇: Akka入门(二)Akka的Actor模