原文:https://doc.akka.io/docs/akka/current/guide/introduction.html?language=scalahtml
Akka是一套開放源碼庫,用於設計可伸縮的、具備彈性的系統,跨越處理器內核和網絡。Akka容許您專一於知足業務需求,而不是編寫低級別代碼來提供可靠的行爲、容錯和高性能。編程
許多常見作法和使用的編程模型並不能解決和設計現代計算機體系結構所固有的重要挑戰。爲了解決那些問題,分佈式系統必須應對組件崩潰不能響應,消息丟失時沒有軌跡,網絡延遲波動。這些問題常常發生在精心管理的數據中心環境中-甚至在虛擬化架構中更是如此。後端
爲了幫助你處理這些現實,Akka提供了:緩存
Akka對Actor模型的使用提供了一個抽象級別,使得編寫正確的併發、並行和分佈式系統變得更容易。角色模型跨越了所有Akka庫,爲您提供了一種理解和使用它們的一致方式。所以,Akka提供了深度的集成,您沒法經過挑選庫來解決單個問題並嘗試將它們組合在一塊兒來實現。安全
經過學習Akka和如何使用Actor模型,您將得到一組普遍而深刻的工具,這些工具能夠在統一的編程模型中解決困難的分佈式/並行系統問題,在這個模型中,一切都緊密而有效地結合在一塊兒。網絡
若是這是您第一次體驗Akka,咱們建議您從運行一個簡單的HelloWorld項目開始。有關下載和運行HelloWorld示例的說明,請參閱QuickStart指南。QuickStart指南向您介紹瞭如何定義角色系統、角色和消息以及如何使用測試模塊和日誌記錄的示例代碼。在30分鐘內,您應該可以運行HelloWorld示例並瞭解它是如何構造的。數據結構
本入門指南提供了下一級別的信息。它涵蓋了爲何角色模型適合現代分佈式系統的須要,幷包括一個教程,將幫助您進一步瞭解Akka。主題包括:多線程
角色模型是幾十年前由卡爾·休伊特(CarlHewitt)提出的,做爲在高性能網絡中處理並行處理的一種方式-這種環境在當時是不可用的。今天,硬件和基礎設施能力已經遇上並超過了休伊特的設想。所以,構建具備嚴格需求的分佈式系統的組織遇到了傳統的面向對象編程(OOP)模型沒法徹底解決的挑戰,但它能夠從角色模型中受益。架構
今天,角色模型不只被認爲是一種很是有效的解決方案-它已經在世界上一些最苛刻的應用程序的生產中獲得了證實。爲了突出角色模型解決的問題,本主題討論傳統編程與現代多線程多CPU體系結構的現實之間的不匹配:併發
OOP的核心支柱是封裝。封裝規定對象的內部數據不能直接從外部訪問;它只能經過調用一組精心設計的方法來修改。該對象負責公開保護其封裝數據的不變性質的安全操做。
例如,對有序二叉樹實現的操做必須不容許違反樹的順序不變。調用方但願順序是完整的,而且當查詢樹以獲取特定的數據時,他們須要可以依賴於這個約束。
當咱們分析OOP運行時行爲時,咱們有時會繪製一個顯示方法調用交互的消息序列圖。例如:
不幸的是,上面的圖表不能準確地表示執行過程當中實例的生命線。實際上,一個線程執行全部這些調用,執行發生在調用方法的在同一個線程上。用執行線程更新關係圖,以下所示:
當您嘗試對多線程所發生的狀況進行建模時,這種澄清的意義就變得清楚了。忽然間,咱們畫得很整齊的圖表變得不夠用了。咱們能夠嘗試說明訪問同一個實例的多個線程:
有一段執行,兩個線程進入相同的方法。不幸的是,對象的封裝模型並不能保證該部分中發生了什麼。這兩個調用的指令能夠任意的方式交織在一塊兒,這就消除了保持變量不變的任何但願,而無需在兩個線程之間進行某種類型的協調。如今,想象一下這個問題因爲存在許多線程而變得更加複雜。
解決此問題的常見方法是圍繞這些方法添加一個鎖。雖然這能夠確保在任何給定的時間最多有一個線程進入該方法,但這是一種代價很高的策略:
這些現實致使了一種不成功的局面:
此外,鎖只能在本地正常工做。當涉及到跨多臺計算機進行協調時,惟一的選擇是分佈式鎖。不幸的是,分佈式鎖的效率比本地鎖低幾個數量級,而且一般對擴展施加一個硬限制。分佈式鎖協議須要在多臺計算機上經過網絡進行屢次通訊,所以延遲時間太高。
在面嚮對象語言中,咱們一般不多考慮線程或線性執行路徑。咱們常常設想一個系統是一個對象實例網絡,它對方法調用做出反應,修改它們的內部狀態,而後經過方法調用相互通訊,從而推進整個應用程序狀態向前:
然而,在多線程分佈式環境中,實際發生的事情是線程經過如下方法調用「遍歷」這個對象實例網絡。所以,真正驅動線程執行的是:
總的來講:
80‘-90年代的編程模型認爲寫入變量意味着直接寫入內存位置(這可能模糊了局部變量可能只存在於寄存器中)。對於現代架構,若是咱們簡化了一些事情,那麼cpu就會編寫緩存行而不是直接寫內存。大多數緩存都是本地cpu核心,也就是說,一個核心寫的都不能被另外一個內核所顯示。爲了使本地更改可見於另外一個核心,所以對於另外一個線程來講,緩存行須要被髮送到其餘核心緩存。
在jvm上,咱們必須顯式地表示經過使用volatile標記或原子包裝來跨線程共享的內存位置。不然,咱們只能在鎖定的部分訪問它們。爲何咱們不把全部變量標記爲volatile?由於傳送高速緩存線跨越核心是一個很是昂貴的操做!這樣作會隱式地將涉及額外工做所涉及的核心拖放,從而致使緩存一致性協議(協議cpu用於傳輸主內存和其餘cpu之間緩存線)瓶頸。結果是減速幅度。
即便對於瞭解這種狀況的開發人員來講,要肯定哪些內存位置應該標記爲volatile,或者使用哪些原子結構是一種黑暗藝術。
總的來講:
今天,咱們常常用call stacks來作理所固然的事。可是,它們是在一個並行編程並不重要,多cpu系統並不常見的時代發明的。call stacks不會跨線程,不模擬異步調用鏈。
當線程打算將任務委託到「後臺」時,會出現問題。實際上,這實際上意味着委託另外一個線程。這不能是簡單的方法/函數調用,由於調用對線程是嚴格的本地調用。一般發生的是,調用者將對象放在一個由工做線程共享的內存位置(「被調用者」),反過來,它會在某些事件循環中從新選擇它。這樣可讓調用方線程繼續運行並執行其餘任務。
第一個問題是,如何才能通知「caller」完成任務?可是當任務與異常失敗時會出現更嚴重的問題。異常傳播到哪裏?它可能將傳播到工做線程的異常處理程序,徹底忽略實際的調用方是誰:
這是一個嚴重的問題,工做線程如何處理這種狀況?它極可能沒法解決這個問題,由於它一般忽略了失敗任務的目的。須要通知「調用方」線程,可是沒有一個調用堆棧以異常解除。故障通知只能經過側通道來完成,例如,在調用「調用方」線程時,將錯誤代碼放在另外一個位置上,不然會預期結果一旦就緒。若是此通知未到位,「調用方」將永遠不會通知失敗,任務丟失!這與網絡系統工做相似,由於在沒有任何通知的狀況下,消息/請求能夠丟失/失敗。
當事情發生錯誤時,這種壞狀況會變得更糟,而一個由線程支持的遇到了一個bug,最終會出現沒法恢復的情況。例如,由bug引起的內部異常,將其拋到root線程,並使線程關閉。這當即引起了一個問題:誰應該從新啓動線程託管服務的正常操做,以及如何恢復到一個已知的狀態?乍一看,這彷佛是能夠控制的,可是咱們忽然面臨一個新的、意想不到的現象:線程當前正在運行的實際任務再也不位於任務從(一般是隊列)中的共享內存位置。事實上,因爲異常到達頂部,解除全部調用堆棧,任務狀態徹底丟失!儘管這是本地通訊,但沒有聯網(預計消息損失將被預期),咱們已經失去了一個消息。
總結:
接下來,讓咱們看看如何使用角色模型來克服這些挑戰。
有什麼討論的內容,能夠加我公衆號: