成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Akka系列(六):Actor解決了什么問題?

Carson / 2137人閱讀

摘要:原文鏈接解決了什么問題使用模型來克服傳統(tǒng)面向?qū)ο缶幊棠P偷木窒扌?,并?yīng)對高并發(fā)分布式系統(tǒng)所帶來的挑戰(zhàn)。在某些情況,這個問題可能會變得更糟糕,工作線程發(fā)生了錯誤但是其自身卻無法恢復。

這段時間由于忙畢業(yè)前前后后的事情,拖更了很久,表示非常抱歉,回歸后的第一篇文章主要是看到了Akka最新文檔中寫的What problems does the actor model solve?,閱讀完后覺得還是蠻不錯,能簡潔清晰的闡述目前并發(fā)領(lǐng)域遇到的問題,并為何利用Actor模型可以解決這些問題,本文主要是利用自己的理解將這篇文章進行翻譯,有不足之處還請指出。原文鏈接

Actor解決了什么問題?

Akka使用Actor模型來克服傳統(tǒng)面向?qū)ο缶幊棠P偷木窒扌裕?yīng)對高并發(fā)分布式系統(tǒng)所帶來的挑戰(zhàn)。 充分理解Actor模型是必需的,它有助于我們認識到傳統(tǒng)的編程方法在并發(fā)和分布式計算的領(lǐng)域上的不足之處。

封裝的弊端

面向?qū)ο缶幊蹋∣OP)是一種廣泛采用的,熟悉的編程模型,它的一個核心理念就是封裝,并規(guī)定對象封裝的內(nèi)部數(shù)據(jù)不能從外部直接訪問,只允許相關(guān)的屬性方法進行數(shù)據(jù)操作,比如我們熟悉的Javabean中的getX,setX等方法,對象為封裝的內(nèi)部數(shù)據(jù)提供安全的數(shù)據(jù)操作。

舉個例子,有序二叉樹必須保證樹節(jié)點數(shù)據(jù)的分布規(guī)則,若你想利用有序二叉樹進行查詢相關(guān)數(shù)據(jù),就必須要依賴這個約束。

當我們在分析面向?qū)ο缶幊淘谶\行時的行為時,我們可能會繪制一個消息序列圖,用來顯示方法調(diào)用時的交互,如下圖所示:

但上述圖表并不能準確地表示實例在執(zhí)行過程中的生命線。實際上,一個線程執(zhí)行所有這些調(diào)用,并且變量的操作也在調(diào)用該方法的同一線程上。為剛才的序列圖加上執(zhí)行線程,看起來像這樣:

但當在面對多線程的情況下,會發(fā)現(xiàn)此前的圖越來越混亂和變得不清晰,現(xiàn)在我們模擬多個線程訪問同一個示例:

在上面的這種情況中,兩個線程調(diào)用同一個方法,但別調(diào)用的對象并不能保證其封裝的數(shù)據(jù)發(fā)生了什么,兩個調(diào)用的方法指令可以任意方式的交織,無法保證共享變量的一致性,現(xiàn)在,想象一下在更多線程下這個問題會更加嚴重。

解決這個問題最通常的方法就是在該方法上加鎖。通過加鎖可以保證同一時刻只有一個線程能進入該方法,但這是一個代價非常昂貴的方法:

鎖非常嚴重的限制并發(fā),它在現(xiàn)在的CPU架構(gòu)上代價是非常大的,它需要操作系統(tǒng)暫停和重啟線程。

調(diào)用者的線程會被阻塞,以致于它不能去做其他有意義的任務(wù),舉個例子我們希望桌面程序在后臺運行的時候,操作UI界面也能得到響應(yīng)。在后臺,,線程阻塞完全是浪費的,有人可能會說可以通過啟動新線程進行補償,但線程也是一種非常昂貴的資源。

使用鎖會導致一個新的問題:死鎖。

這些現(xiàn)實存在的問題讓我們只能兩者選一:

不使用鎖,但會導致狀態(tài)混亂。

使用大量的鎖,但是會降低性能并很容易導致死鎖。

另外,鎖只能在本地更好的利用,當我們的程序部署在不同的機器上時,我們只能選擇使用分布式鎖,但不幸的是,分布式鎖的效率可能比本地鎖低好幾個量級,對后續(xù)的擴展也會有很大的限制,分布式鎖的協(xié)議要求多臺機器在網(wǎng)絡(luò)上進行相互通信,因此延遲可能會變得非常高。

在面向?qū)ο笳Z言中,我們很少會去考慮線程或者它的執(zhí)行路徑,我們通常將系統(tǒng)想象成許多實例對象連接成的網(wǎng)絡(luò),通過方法調(diào)用,修改實例對象內(nèi)部的狀態(tài),然后通過實例對象之前的方法調(diào)用驅(qū)動整個程序進行交互:

然后,在多線程分布式環(huán)境中,實際上線程是通過方法調(diào)用遍歷這個對象實例網(wǎng)絡(luò)。因此,線程是方法調(diào)用驅(qū)動執(zhí)行的:

總結(jié):

對象只能保證在單一線程中封裝數(shù)據(jù)的正確性,在多線程環(huán)境下可能會導致狀態(tài)混亂,在同一個代碼段,兩個競爭的線程可能導致變量的不一致。

使用鎖看起來可以在多線程環(huán)境下保證封裝數(shù)據(jù)的正確性,但實際上它在程序真是運行時是低效的并且很容易導致死鎖。

鎖在單機工作可能還不錯,但是在分布式的環(huán)境表現(xiàn)的很不理想,擴展性很差。

共享內(nèi)存在現(xiàn)代計算機架構(gòu)上的弊端

在80-90年代的編程模型概念中,寫一個變量相當于直接把它寫入內(nèi)存,但是在現(xiàn)代的計算機架構(gòu)中,我們做了一些改變,寫入相應(yīng)的緩存中而不是直接寫入內(nèi)存,大多數(shù)緩存都是CPU核心的本地緩存,但是由一個CPU寫入的緩存對其他CPU是不可見的。為了讓本地緩存的變化對其他CPU或者線程可見的話,緩存必須進行交互。

在JVM上,我們必須使用volatile標識或者Atomic包裝類來保證內(nèi)存對跨線程的共享,否則,我們只能用鎖來保證共享內(nèi)存的正確性。那么我們?yōu)槭裁床辉谒械淖兞可隙技觱olatile標識呢?因為在緩存間交互信息是一個代價非常昂貴的操作,而且這個操作會隱式的阻止CPU核心不能去做其他的工作,并且會導致緩存一致性協(xié)議(緩存一致性協(xié)議是指CPU用于在主內(nèi)存和其他CPU之間傳輸緩存)的瓶頸。

即使開發(fā)者認識到這些問題,弄清楚哪些內(nèi)存位置需要使用volatile標識或者Atomic包裝類,但這并非是一種很好的解決方案,可能到程序后期,你都不清楚自己做了什么。

總結(jié):

沒有真正的共享內(nèi)存了,CPU核心就像網(wǎng)絡(luò)上的計算機一樣,將數(shù)據(jù)塊(高速緩存行)明確地傳遞給彼此。CPU間的通信和網(wǎng)絡(luò)通信有更多的共同點。 現(xiàn)在通過CPU或網(wǎng)絡(luò)計算機傳遞消息是標準的。

使用共享內(nèi)存標識或者Atomic數(shù)據(jù)結(jié)構(gòu)來代替隱藏消息傳遞,其實有一種更加規(guī)范的方法就是將共享狀態(tài)保存在并發(fā)實體內(nèi),并明確并發(fā)實體間通過消息來傳遞事件和數(shù)據(jù)。

調(diào)用堆棧的弊端

今天,我們還經(jīng)常調(diào)用堆棧來進行任務(wù)執(zhí)行,但是它是在并發(fā)并不那么重要的時代發(fā)明的,因為當時多核的CPU系統(tǒng)并不常見。調(diào)用堆棧不能跨線程,所以不能進行異步調(diào)用。

線程在將任務(wù)委托后臺執(zhí)行會出現(xiàn)一個問題,實際中,是將任務(wù)委托給另一個線程執(zhí)行,這不是簡單的方法調(diào)用,而是有本地的線程直接調(diào)用執(zhí)行,通常來說,一個調(diào)用者線程將任務(wù)添加到一個內(nèi)存位置中,具體的工作線程可以不斷的從中選取任務(wù)進行執(zhí)行,這樣的話,調(diào)用者線程不必阻塞可以去做一些其他的任務(wù)了。

但是這里有幾個問題,第一個就是調(diào)用者如何受到任務(wù)完成的通知?還有一個更重要的問題是當任務(wù)發(fā)生異常出現(xiàn)錯誤后,異常會被誰處理?異常將會被具體執(zhí)行任務(wù)的工作線程所處理并不會關(guān)心是哪個調(diào)用者調(diào)用的任務(wù):

這是一個很嚴重的問題,具體執(zhí)行任務(wù)的線程是怎么處理這種狀況的?具體執(zhí)行任務(wù)去處理這個問題并不是一個好的方案,因為它并不清楚該任務(wù)執(zhí)行的真正目的,而且調(diào)用者應(yīng)該被通知發(fā)生了什么,但是實際上并沒有這樣的結(jié)構(gòu)去解決這個問題。假如并不能正確的通知,調(diào)用者線程將不會的到任何錯誤的信息甚至任務(wù)都會丟失。這就好比在網(wǎng)絡(luò)上你的請求失敗或者消息丟失卻得不到任何的通知。

在某些情況,這個問題可能會變得更糟糕,工作線程發(fā)生了錯誤但是其自身卻無法恢復。比如一個由bug引起的內(nèi)部錯誤導致了線程的關(guān)閉,那么會導致一個問題,到底應(yīng)該由誰來重啟線程并且保存線程之前的狀態(tài)呢?表面上看,這個問題是可以解決的,但又會有一個新的意外可能發(fā)生,當工作線程正在執(zhí)行任務(wù)的時候,它便不能共享任務(wù)隊列,而事實上,當一個異常發(fā)生后,并逐級上傳,最終可能導致整個任務(wù)隊列的狀態(tài)全部丟失。所以說即使我們在本地交互也可能存在消息丟失的情況。

總結(jié):

實現(xiàn)任何一個高并發(fā)且高效性能的系統(tǒng),線程必須將任務(wù)有效率的委托給別的線程執(zhí)行以至不會阻塞,這種任務(wù)委托的并發(fā)方式在分布式的環(huán)境也適用,但是需要引入錯誤處理和失敗通知等機制。失敗成為這種領(lǐng)域模型的一部分。

并發(fā)系統(tǒng)適用任務(wù)委托機制需要去處理服務(wù)故障也就意味需要在發(fā)生故障后去恢復服務(wù),但實際情況下,重啟服務(wù)可能會丟失消息,即使沒有發(fā)生這種情況,調(diào)用者得到的回應(yīng)也可能因為隊列等待,垃圾回收等影響而延遲,所以,在真正的環(huán)境中,我們需要設(shè)置請求回復的超時時間,就像在網(wǎng)絡(luò)系統(tǒng)亦或者分布式系統(tǒng)。

為什么在高并發(fā),分布式系統(tǒng)需要Actor模型?

綜上所述,通常的編程模型并不適用現(xiàn)代的高并發(fā)分布式系統(tǒng),幸運的是,我們可以不必拋棄我們了解的知識,另外,Actor用很好的方式幫我們克服了這些問題,它讓我們以一種更好的模型去實現(xiàn)我們的系統(tǒng)。

我們重點需求的是以下幾個方面:

使用封裝,但是不使用鎖。

構(gòu)建一種實體能夠處理消息,更改狀態(tài),發(fā)送消息用來推動整個程序運行。

不必擔心程序執(zhí)行與真實環(huán)境的不匹配。

Actor模型能幫我們實現(xiàn)這些目標,以下是具體描述。

使用消息機制避免使用鎖以防止阻塞

不同于方法調(diào)用,Actor模型使用消息進行交互。發(fā)送消息的方式不會將發(fā)送消息方的執(zhí)行線程轉(zhuǎn)換為具體的任務(wù)執(zhí)行線程。Actor可以不斷的發(fā)送和接收消息但不會阻塞。因此它可以做更多的工作,比如發(fā)送消息和接收消息。

在面對對象編程上,直到一個方法返回后,才會釋放對調(diào)用者線程的控制。在這這一方面上,Actor模型跟面對對象模型類似,它對消息做出處理,并在消息處理完成后返回執(zhí)行。我們可以模擬這種執(zhí)行模式:

但是這種方式與方法調(diào)用方式最大的區(qū)別就是沒有返回值。通過發(fā)送消息,Actor將任務(wù)委托給另一Actor執(zhí)行。就想我們之前說的堆棧調(diào)用一樣,加入你需要一個返回值,那么發(fā)送Actor需要阻塞或者與具體執(zhí)行任務(wù)的Actor在同一個線程中。另外,接收Actor以消息的方式返回結(jié)果。

第二個關(guān)鍵的變化是繼續(xù)保持封裝。Actor對消息處理就就跟調(diào)用方法一樣,但是不同的是,Actor在多線程的情況下能保證自身內(nèi)部的狀態(tài)和變量不會被破壞,Actor的執(zhí)行獨立于發(fā)送消息的Actor,并且同一個Actor在同一個時刻只處理一個消息。每個Actor有序的處理接收的消息,所以一個Actor系統(tǒng)中多個Actor可以并發(fā)的處理自己的消息,充分的利用多核CPU。因為一個Actor同一時刻最多處理一個消息,所以它不需要同步機制保障變量的一致性。所以說它并不需要鎖:

總而言之,Actor執(zhí)行的時候會發(fā)生以下行為:

1.Actor將消息加入到消息隊列的尾部。
2.假如一個Actor并未被調(diào)度執(zhí)行,則將其標記為可執(zhí)行。
3.一個(對外部不可見)調(diào)度器對Actor的執(zhí)行進行調(diào)度。
4.Actor從消息隊列頭部選擇一個消息進行處理。
5.Actor在處理過程中修改自身的狀態(tài),并發(fā)送消息給其他的Actor。
6.Actor

為了實現(xiàn)這些行為,Actor必須有以下特性:

郵箱(作為一個消息隊列)

行為(作為Actor的內(nèi)部狀態(tài),處理消息邏輯)

消息(請求Actor的數(shù)據(jù),可看成方法調(diào)用時的參數(shù)數(shù)據(jù))

執(zhí)行環(huán)境(比如線程池,調(diào)度器,消息分發(fā)機制等)

位置信息(用于后續(xù)可能會發(fā)生的行為)

消息會被添加到Actor的信箱中,Actor的行為可以看成Actor是如何對消息做出回應(yīng)的(比如發(fā)送更多消息或者修改自身狀態(tài))。執(zhí)行環(huán)境提供一組線程池,用于執(zhí)行Actor的這些行為操作。

Actor是一個非常簡單的模型而且它可以解決先前提到的問題:

繼續(xù)使用封裝,但通過信號機制保障不需傳遞執(zhí)行(方法調(diào)用需要傳遞執(zhí)行線程,但發(fā)送消息不需要)。

不需要任何的鎖,修改Actor內(nèi)部的狀態(tài)只能通過消息,Actor是串行處理消息,可以保障內(nèi)部狀態(tài)和變量的正確性。

因為不會再任何地方使用鎖,所以發(fā)送者不會被阻塞,成千上萬個Actor可以被合理的分配在幾十個線程上執(zhí)行,充分利用了現(xiàn)代CPU的潛力。任務(wù)委托這個模式在Actor上非常適用。

Actor的狀態(tài)是本地的,不可共享的,變化和數(shù)據(jù)只能通過消息傳遞。

Actor優(yōu)雅的處理錯誤

Actor不再使用共享的堆棧調(diào)用,所以它要以不同的方式去處理錯誤。這里有兩種錯誤需要考慮:

第一種情況是當任務(wù)委托后再目標Actor上由于任務(wù)本身錯誤而失敗了(典型的如驗證錯誤,比如不存在的用戶ID)。在這個情況下,Actor服務(wù)本身是正確的,只是相應(yīng)的任務(wù)出錯了。服務(wù)Actor應(yīng)該想發(fā)送Actor發(fā)送消息,已告知錯誤情況。這里沒什么特殊的,錯誤作為Actor模型的一部分,也可以當做消息。

第二種情況是當服務(wù)本身遇到內(nèi)部故障時。Akka強制所有Actor被組織成一個樹狀的層次結(jié)構(gòu),即創(chuàng)建另一個Actor的Actor成為該新Actor的分級。 這與操作系統(tǒng)將流程組合到樹中非常相似。就像進程一樣,當一個Actor失敗時,它的父actor被通知,并對失敗做出反應(yīng)。此外,如果父actor停止,其所有子Actor也被遞歸停止。這中形式被稱為監(jiān)督,它是Akka的核心:

監(jiān)管者可以根據(jù)被監(jiān)管者(子Actor)的失敗的錯誤類型來執(zhí)行不同的策略,比如重啟該Actor或者停止該Actor讓其它Actor代替執(zhí)行任務(wù)。一個Actor不會無緣無故的死亡(除非出現(xiàn)死循環(huán)之類的情況),而是失敗,并可以將失敗傳遞給它的監(jiān)管者讓其做出相應(yīng)的故障處理策略,當然也可能會被停止(若被停止,也會接收到相應(yīng)的消息指令)。一個Actor總有監(jiān)管者就是它的父級Actor。Actor被重新啟動是不可見的,協(xié)作Actor可以幫其代發(fā)消息直到目標Actor重啟成功。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/67327.html

相關(guān)文章

  • Akka系列(一):Akka簡介與Actor模型

    摘要:是一個構(gòu)建在上,基于模型的的并發(fā)框架,為構(gòu)建伸縮性強,有彈性的響應(yīng)式并發(fā)應(yīng)用提高更好的平臺。上述例子中的信件就相當于中的消息,與之間只能通過消息通信。當然模型比這要復雜的多,這里主要是簡潔的闡述一下模型的概念。模型的出現(xiàn)解決了這個問題。 Akka是一個構(gòu)建在JVM上,基于Actor模型的的并發(fā)框架,為構(gòu)建伸縮性強,有彈性的響應(yīng)式并發(fā)應(yīng)用提高更好的平臺。本文主要是個人對Akka的學習和應(yīng)...

    PingCAP 評論0 收藏0
  • Akka系列(四):Akka中的共享內(nèi)存模型

    摘要:共享內(nèi)存相信對并發(fā)有所了解的同學都應(yīng)該知道在推出后,對內(nèi)存管理有了更高標準的規(guī)范了,這使我們開發(fā)并發(fā)程序也有更好的標準了,不會有一些模糊的定義導致的無法確定的錯誤。 通過前幾篇的學習,相信大家對Akka應(yīng)該有所了解了,都說解決并發(fā)哪家強,JVM上面找Akka,那么Akka到底在解決并發(fā)問題上幫我們做了什么呢? 共享內(nèi)存 眾所周知,在處理并發(fā)問題上面,最核心的一部分就是如何處理共享內(nèi)存,...

    baukh789 評論0 收藏0
  • Akka系列(七):Actor持久化之Akka persistence

    摘要:源碼鏈接進階持久化插件有同學可能會問,我對不是很熟悉亦或者覺得單機存儲并不是安全,有沒有支持分布式數(shù)據(jù)存儲的插件呢,比如某爸的云數(shù)據(jù)庫答案當然是有咯,良心的我當然是幫你們都找好咯。 這次把這部分內(nèi)容提到現(xiàn)在寫,是因為這段時間開發(fā)的項目剛好在這一塊遇到了一些難點,所以準備把經(jīng)驗分享給大家,我們在使用Akka時,會經(jīng)常遇到一些存儲Actor內(nèi)部狀態(tài)的場景,在系統(tǒng)正常運行的情況下,我們不需要...

    miguel.jiang 評論0 收藏0
  • Akka系列(二):Akka中的Actor系統(tǒng)

    摘要:模型作為中最核心的概念,所以在中的組織結(jié)構(gòu)也至關(guān)重要,本文主要介紹中系統(tǒng)。這里主要是演示可以根據(jù)配置文件的內(nèi)容去加載相應(yīng)的環(huán)境,并應(yīng)用到整個中,這對于我們配置環(huán)境來說是非常方便的。路徑與地址熟悉類系統(tǒng)的同學應(yīng)該對路徑這個概念很熟悉了。 Actor模型作為Akka中最核心的概念,所以Actor在Akka中的組織結(jié)構(gòu)也至關(guān)重要,本文主要介紹Akka中Actor系統(tǒng)。 Actor系統(tǒng) Act...

    BlackFlagBin 評論0 收藏0
  • Akka系列(三):監(jiān)管與容錯

    摘要:是所有由系統(tǒng)創(chuàng)建的頂級的監(jiān)管者,如日志監(jiān)聽器,或由配置指定在系統(tǒng)啟動時自動部署的。所有其他被上升到根監(jiān)管者,然后整個系統(tǒng)將會關(guān)閉。監(jiān)管容錯示例本示例主要演示在發(fā)生錯誤時,它的監(jiān)管者會根據(jù)相應(yīng)的監(jiān)管策略進行不同的處理。 Akka作為一種成熟的生產(chǎn)環(huán)境并發(fā)解決方案,必須擁有一套完善的錯誤異常處理機制,本文主要講講Akka中的監(jiān)管和容錯。 監(jiān)管 看過我上篇文章的同學應(yīng)該對Actor系統(tǒng)的工作...

    shevy 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<