Microsoft Orleans 之 入門指南


前言:Orleans 提供了構建大型分布式運算的應用程序框架,而不需要學習那些復雜的並發或者可拓展的模式。它由微軟研究院創建和維護,並且它已被一些微軟產品組廣泛應用於微軟的Azure,以及其他一些公司。

        Orleans 在2015年一月微軟將其源代碼開源,迅速得到開發者的認可,借助於活躍的社區和開發團隊的貢獻,框架不斷的被完善和新功能的增加,微軟研究院也將一直為該項目投資下去。

背景:雲應用程序和服務本質上是並行的和分布式的。他們也互動性和動態性,往往需要近實時的雲實體之間的直接相互作用。對於今天的程序構建來說這樣的應用是非常困難的。隨着工作量的增長,通常需要昂貴的設計和架構的迭代,開發過程需要專家級的程序員。

  今天大多數高性能應用程序是通過標准的SOA構建的,比如,亞馬遜、google或者Facebook 一個簡單頁面的渲染展示,是通過數以百計的復雜的SOA服務協作和管理。事實上,這樣一個復雜的服務是很難通過一個服務來構建完成的。

     SOA的模塊化服務化、大數據、高並發已經服務動態分配和不間斷的運行,不停的挑戰程序員處理這些問題的能力,但是現有提供的這些工具或者框架不足以解決這些問題。

      無狀態的多層模型到存儲層划分問題。它經常需要在無狀態層中進行緩存以獲得可接受的性能,增加了復雜性,並引入了緩存一致性問題。

Actors: Actor 模型對細粒度的單個Actor對象的支持很好,每個Actor對象之間彼此孤立並且都是輕量級的,可以保證這些Actor獨立建模。他們通過異步消息傳遞,從而使Actor彼此直接溝通。

       值得注意的是,一個Actor是在邏輯意義的單線程上執行,同時Actor的狀態的封裝和其他Actor之間的隔離,這大大的簡化了Actor代碼層面的復雜性,開發者使用的Actor不必擔心臨界區、互斥鎖,鎖的水准,和其他復雜的難以預測的問題,這些與實際應用程序邏輯是無關的,Actor是在可用的硬件資源池內動態創建的。這使得Actors模型在負載均衡上比SOA的模塊化分割更容易維護和拓展。

  在過去的十年里,Erlang 一直是傳統Actor模式的最流行的實現,面對上述SOA的挑戰,業內重新審視Actor 模式,這促使出現了新的解決方案:Scala actors,Akka,DCell

Virtual Actors:

Orleans 是一種改進的Actors模式,大量借鑒了Erlang和分布式對象系統中的實現,添加靜態類型,消息indirection 和Actors的虛擬化,將它們暴露在一個集成的編程模型中。

而Erlang是一個純粹的函數式語言有其自己特定的虛擬機,Orleans 的編程模型,直接利用.NET 和面向對象的能力,它提供了一個框架,使復雜的分布式應用程序的開發更容易,並使所得到的應用程序的可擴展性更強。

不像其他系統如Erlang或Akka 的Actors , Orleans Grains 的Actors 是虛擬Actors。他們通過異步消息傳遞,這大大不同於同步方法調用。

在Orleans 框架里 grains 的管理和激活類似於操作系統的虛擬內存管理,grain 的激活是通過創建一個服務端的copy ,如果后續它不再使用的時就自動變為未激活狀態。

如果一個消息被發送到grain且在任何服務器上都沒有激活狀態的grain時,運行時管理器將自動創建一個新的grain,因為grains是虛擬的,所以他們永遠不會失敗,即使目前主機所有服務器激活失敗。

這使得沒必要測試是否有grain存在的必要性,以及故障跟蹤;Orleans 運行時做這一切都是自動的。

更多的內容細節請閱讀如下連接

Read the MSR Technical Report on Orleans

優點:

 Orleans的優點有  1,提高開發人員的開發效率,即使不是專業開發人員。

                         2,不需要做太多的工作,默認支持很強的可拓展性。

下面詳細講解這些優點

開發人員的生產率

Orleans 的編程模型提供了以下關鍵的抽象,提出了專家和非專家的程序員的生產率,保證系統服務。

1.遵循面向對象的規范:Actor是實現聲明的.net類,與Actors接口的異步方法,因此,開發人員可以把Actor的方法作為遠程對象的方法直接調用。透明地調用Actor的方法和故障的處理。

2.單線程執行:確保Actor只在一個線程上執行,與其他Actor的隔離,保證不用擔心並發的問題,也不會擔心使用數據共享而必須使用鎖機制等問題,對於非專家程序員開發分布式程序變得更容易。

3.透明激活:只有當需要處理一個消息時,運行時才激活一個Actor,區分開了通過代碼控制創建Actor引用 和通過物理內存激活Actor 的概念,這中機制在應用程序中是透明的,開發人員不需要關系一個Actor是如何創建的。很多適合一個Actor的激活或者銷毀,是由虛擬內存決定的。應用程序不斷輪訓所有的“內存空間”的邏輯上創建的Actors,無論他們是在物理內存在任何特定的點。Actor的生命周期是應用管理,此功能明顯改善了傳統Actor模型。

4.位置透明:通過Actor的對象引用來調用其他機器上的包含業務邏輯的Actor,在Orleans 運行時,這兩個Actor之間是相互透明的。

  如果應用程序找不到一個Actor,這有可能是當前這個Actor被垃圾回收了或者是還沒有被激活。

5.透明集成持久化存儲:Orleans 允許發布持久化到內存中的Actor的狀態的映射,它同步更新確保成功持久化到內存中后可以獲得一個正確的結果。

可以直接拓展或者更改現有持久化機制。

6.錯誤自動傳播:運行時調用鏈上分布式的try/catch將未處理的錯誤與異常自動處理。因此,錯誤不會在應用程序中丟失。這允許開發人員在適當的地方放置錯誤處理邏輯,而無需在每個級別手動傳播錯誤。

 默認的透明的可擴展性

實踐證明Orleans 編程模型 提供一個較低級別的系統功能就可以很輕易的實現開發人員的程序拓展和服務器拓展,下面詳解在性能和拓展點上的關鍵點:

1.應用狀態的細粒度的分割:

通過使用Actor作為直接尋址實體,程序員隱式地打破了他們的應用程序的整體狀態,雖然orleans 沒有明確指出如何划分一個大或者小的Actor模型,但是在大多數情況下,每一個自然程序實體代表一個Actor來多划分出更多的Actor來說是有好處的,比如一個用戶賬戶、一個訂單等等。隨着Actor被單獨尋址,它們的物理邏輯在運行時被抽象出來了

2.自適應資源管理:假設在負載均衡和通信的請求不失敗的情況下因為Actor位置的透明或者運行時的管理動態調整了硬件資源的配置 決定遷移整個計算集群的Actors,而相互通信的Actor之間沒有彼此的位置,可以根據吞吐量的需要動態創建一個或多個運行時的演員特定的Actor副本,而無需對應用程序代碼進行任何更改。

3.多路轉換通信:在Orleans Actors的邏輯終結點(endpoints)和消息,他們之間是復用在一組固定的所有Tcp連接(TCP sockets),這使得主機一組數量龐大的Actors組的尋址不會影響到系統的性能,另外actors的激活和停用不會影響物理終結點的注冊/注銷的性能成本,如TCP端口或HTTP URL,甚至關閉TCP連接。

4.高效的調度:大量單線程Actors的運行時的調度執行運行在每個處理器核心的的自定義線程池中,和用非阻斷連續Actor的代碼(在Orleans 的編程模型必須的)在一個高效協作多線程方式運行應用程序代碼沒有競爭。這使得系統能夠達到高吞吐量和大大提高了中央處理器的利用率(高達90%),具有極高的穩定性。

事實上,在系統中的Actor的數量的增長和負載不會導致額外的線程或其他操作系統有助於單個節點和整個系統的可擴展性。

5.顯式同步:Orleans 的編程模式明確指導開發人員使用異步執行編寫異步非阻塞代碼的分布式應用程序,

Orleans 的編程模型使分布式應用程序的異步性和指導程序員編寫的非阻塞異步代碼,結合異步消息傳遞和高效調度,這使一個大程度的分布式並行性和整體吞吐量,沒有明確的使用多線程。

Grains:

分布式的單位Grains(Actors)

並發是分布式應用程序固有的特點,這使得增加了它的復雜性,而Actor模型的特殊性和具有創造性的特點,使這樣的事情變得簡單了。

Actor以兩種方式做到這一點:

1.通過提供單個線程訪問一個Actor實例的內部狀態。

2.除通過消息傳遞外,不在Actor實例之間共享數據。

Grains 是Orleans 應用程序的構建塊,它們是彼此孤立的原子單位,分布的,持久的。一個典型的grain是有狀態和行為的一個單實例。

執行單元:單線程執行模型背后的原理是Actor(remote)輪流調用其方法。如,從Actor A 到 Actor B的消息將被放置在一個隊列中 ,當所有優先級比較高的服務處理完成才去調用隊列相關的處理程序。這使我們能夠避免所有使用鎖來保護Actor的狀態,因為它本質上是保護,防止數據競爭。然而,它也可能會出現一些問題,消息的循環傳遞。如:如果A發送消息給B從它的一個方法,並等待它的完成,和B發送一個消息,也等待其完成,應用程序就將會出現死鎖的問題。

 Grains 激活-grain運行時實例

當運行一個grain的時候,Orleans 確保在一個Orleans silos 有一個grain的一個實例,當發現任何的silo中都沒有一個grain的一個實例時,運行時就會創建一個,這個過程被稱為激活。

如果使用的是一個已經持久化了的grain,運行時就自動去后台讀取並激活,Orleans 控制着整個激活或者停用的過程,當編碼一個grain時,開發人員假定所有的grain都是被激活的。

grain激活在塊中執行工作,並在它移動到下一個之前完成每一個塊。大塊的工作包括響應來自其他grain或外部客戶請求的方法調用,並止於前一塊完成關閉。對應於一個工作塊的基本的執行單元被稱為一個turn。

在並行期間orleans 執行很多屬於不同的激活的turn,每個激活是允許同時執行它的turns,這意味着沒必要使用鎖或者其他同步的方法去阻止數據的爭奪和多線程的危險,如上所述,無論如何,預定關閉的truns的不可預測的交錯,可能會導致grain的狀態與計划關閉時時不同的,所以開發人員仍然需要注意交錯錯誤。

激活模式

Orleans 支持兩種模式:

1.單激活模式(默認):單激活模式(默認),創建每一個grain的同時激活。

2.無邊界工作模式:創建自主激活的grain,以提高吞吐量。 “自主”意味着有相同grain不同的激活之間狀態不一致。因此,這種模式適合於無本地狀態保留grain,或grain的本地狀態是不變的,比如作為持久態的高速緩存grain

Silos:

Orleans silo 是一個主機服務,用來執行Orleans grains,它監聽一個端口,用來監聽從silo到silo的消息或者從其他客戶端到silo的消息的,典型的silo就是,每台機器運行一個silo。

cluster:

大量的silos 同時在一起工作就形成了orleans的集群,orleans運行完全自動化的集群管理。

所有silo都使用一個動態更新的共享成員存儲庫,並有助於協調集群管理,通過閱讀共享存儲庫了解對方的位置,在任何時候,一個silo可以通過注冊在共享存儲中連接到一個集群。

這種方式的集群可以在運行時動態擴展。Orleans 提供彈性和可用性從群集中刪除無效的silos。

對於Orleans 管理集群深入詳細的文檔,請閱讀集群管理。

接下來我們看看Orleans框架客戶端的使用 。

Clients:

Orleans 和客戶端代碼

Orleans 包括兩個不同的部分:Orleans 基礎部分(grains) 和客戶端部分

Orleans 的一部分是由應用程序的運行時服務稱silos grains 組成,在調度限制下的運行時執行的Grain代碼和確保內部在Orleans 編程模型。

客戶端部分通常是一個web前端,通過少量的Orleans 客戶端庫連接到Orleans 部分,使得客戶端代碼可以通過引用服務端的一個grain的引用進行通訊。

例如:一個ASP.NET web應用程序運行在服務端的部分可以是Orleans 的客戶端。客戶端部分運行在.net 應用程序池的主線程中,和不受調度的限制和Orleans 運行時的保證。

下一步,我們將看看一個grains如何接收異步消息,或推送數據。

客戶端觀察者

 有一種情況,其中一個簡單的消息/響應模式是不夠的,客戶端需要接收異步通知。例如,一個用戶可能希望在一個新的即時消息發布的時候被一個朋友通知。

客戶端觀察者是一允許異步通知客戶端的一種機制。觀察者是一個繼承自IGrainObserver的單向異步接口,它的所有方法都的返回值必須是void。grain 給這個觀察者發送一個通知是通過調用這個接口的一個方法,除了它沒有返回值,因此grain不需要依賴於結果,Orleans運行時將確保單項通知,grain 發布了這樣的一個通知同時也提供了相應的add observers或者remove observers的 API。

另外,通常它也提供了一些使用的用來取消這些訂閱的方法,Grain 開發者可以使用Orleans 的這個ObserverSubscriptionManager<T>泛型類簡化observed 的grain類型的開發。

訂閱一個通知,客戶端必須先創建一個實現了觀察者接口的C#本地類的對象,它調用觀察者工廠的方法(CreateObjectReference()),重定向到C#對象進入grain對象引用,然后可以將其傳遞給通知grain的訂閱方法。

該模型也可以被其他grains用於接收異步通知。不像在客戶端訂閱的情況,訂閱grain只是實現Observer接口的一個面,並通過自身的引用(如:this.AsReference<IMyGrainObserverInterface>)

代碼示例

假設我們有一個周期性發送信息給客戶的grain,為簡單起見,在我們的例子中的消息將是一個字符串。我們首先定義客戶端接收消息的的接口。

該接口將看起來像這樣

public interface IChat : IGrainObserver { void ReceiveMessage(string message); }


唯一特殊的是繼承了IGrainObserver,現在任何用戶想觀察這些信息應該實現一個實現了IChat接口的類。
如下:
public class Chat : IChat { public void ReceiveMessage(string message) { Console.WriteLine(message); } }


現在在服務器上,我們應該有一個grain,發送這些聊天信息給客戶端。grain也應該有一個為客戶端訂閱和退訂的消息機制。
訂閱的grain可以用類ObserverSubscriptionManager:
class HelloGrain : Grain, IHello { private ObserverSubscriptionManager<IChat> _subsManager; public override async Task OnActivateAsync() { // We created the utility at activation time. _subsManager = new ObserverSubscriptionManager<IChat>(); await base.OnActivateAsync(); } // Clients call this to subscribe. public async Task Subscribe(IChat observer) { _subsManager.Subscribe(observer); } //Also clients use this to unsubscribe themselves to no longer receive the messages. public async Task UnSubscribe(IChat observer) { _SubsManager.Unsubscribe(observer); } }

可以使用ObserverSubscriptionManager<IChat>的實例給客戶端發送通知消息,這個發送消息的方法接受一個Action<T>的委托或者一個表達式,您可以調用接口上的任何方法,將其發送給客戶端。
在我們的案例只有一個方法ReceiveMessage,我們的服務器上的發送代碼會看起來像這樣:
public Task SendUpdateMessage(string message) { _SubsManager.Notify(s => s.ReceiveMessage(message)); return TaskDone.Done; }


現在我們的服務器來發送信息觀察客戶的方法,用於訂閱/退訂和客戶端兩種方法實現了一個類能夠觀察grain的消息。最后一步是創建基於我們以前實現的聊天類客戶觀察員 Chat 引用 ,並讓它收它訂閱的消息。

代碼看起來像這樣:
//First create the grain reference var friend = GrainClient.GrainFactory.GetGrain<IHello>(0); Chat c = new Chat(); //Create a reference for chat usable for subscribing to the observable grain. var obj = await GrainClient.GrainFactory.CreateObjectReference<IChat>(c); //Subscribe the instance to receive messages. await friend.Subscribe(obj);


現在我們的服務器上調用grain的方法SendUpdateMessage ,所有訂閱的客戶端將收到消息。在我們的客戶代碼,Chat 的實例將變量c輸出到控制台。
注意:支持觀察員可能會在未來的版本中刪除,取而代之的是一個簡單的消息流短信,它可以支持相同的概念,更多的靈活性和可靠性。

常見問題:

微軟是否支持orleans?

orleans的源代碼已經在GitHub上的MIT許可下發布。微軟將繼續投資在orleans和接受社區的代碼貢獻。

可以獲的一個“啟動”的License?

License 已經放在源代碼releases 下

Orleans 適用於生產環境嗎?

答案是肯定的

什么時候使用Grain 什么時候使用一個普通對象?

有兩種方法來回答這個問題,運行時和建模的角度。

從運行時角度看:grain內創建的對象是不能遠程調用,Grain 的位置透明在系統的任何位置都可以訪問,這樣他們就可以被自動放置或部署任何服務器,和當他們寄宿的服務器故障時可以重啟。

從建模的角度看:在你的 Orleans-based應用程序中有四種基本的組件,通信接口、消息載體、grains和grains的數據保存,對象用戶承載grains的數據,通信接口是一些有一定限制的常規接口。

仍然存在一些問題就是,在一個給定的系統中的實體應被建模為grain嗎?

一般來說,你應該使用一個grain來模擬一個獨立的實體,它有一個公開的通信接口與系統的其他組件,並有自己的生命周期,即,它可以獨立存在於其他組件中。例如,在社會網絡中的一個用戶是一個grain,而它的名字不是。用戶的新聞牆可能是一個grain,而它所接收到的消息的列表不是(因為牆是由其他用戶訪問,而列表的消息是一個私人數據)。希望這個網站上的樣本將有助於你識別一些模式,並看到你自己的場景的相似之處。

如何提高Grain 的高訪問?

一個grain的吞吐量是有限的,它是運行在一個單線程中,因此,最好是避免設計一個單一的grain來接收不成比例的請求。

有各種各樣的模式,有助於防止單一grain的超載,即使在邏輯上它是一個通信的中心點。

例如:如果grain是 一個 大量grains的 統計或者計數的聚合器,一個行之有效的方法是增加一個控制數量的中間聚合grains和分配每個報告的grains(使用module在key或hashcode)到中間聚合器,使負載或多或少均勻地分布在所有中間聚合器的grains,在他們的定期部分聚集到中央聚合器grains

 

如何取消或者是取消激活grain?

一般不需要強制一個grain失活,作為orleans運行時自動檢測並關閉閑置激活的grain並回收系統資源。在特殊的情況下,當你認為你需要立即使grain失活時,可以通過調用基類方法base.DeactivateOnIdle()。

我可以告訴orleans,在哪里激活grain嗎?

它可以通過限制策略( restrictive placement strategies)來實現,但我們一般認為這是一個反模式,所以不建議。如果你發現自己需要指定激活特定的silo grain,應該讓你的系統來處理,這樣就可以充分利用orleans框架。

通過解決這樣的問題,表明應用程序承擔了資源管理器的負擔,而不一定有足夠的信息系統的全局狀態多的好。

可以多個數據中心獨立部署嗎?

orleans部署目前僅限於一個單一的數據中心。

我可以熱部署grain添加或更新他們嗎?

目前是不可以。

如何升級grains的版本?

orleans目前不支持即時更新grain代碼。要升級到新版本的grain代碼,您需要提供並啟動一個新的部署,切換過來,然后關閉舊部署。

雲緩存服務中可以保存grain的狀態嗎?

它可以通過雲存儲,但是默認沒有提供,你可以自己創建一個。

我可以從公共網絡連接到orleans 的 slios?

orleans是托管服務的后端部分,你應該創建一個前端服務器的客戶端連接到你的服務端。它可以基於Web API項目,HTTP,Socket服務器,一個signalr服務器或其他東西。你真的可以從Internet連接到orleans,但從安全的角度考慮,不是一個好的實踐。

如果調用一個grain返回之前slio失敗,會發生什么?

您將收到一個異常,您可以捕獲和重試或做任何其他在您的應用程序邏輯中有意義的。原文如下,

You’ll receive an exception which you can catch and retry or do anything else which makes sense in your application logic. The Orleans runtime does not immediately recreate grains from a failed silo because many of them may not be needed immediately or at all. Instead, the runtime recreates such grains individually and only when a new request arrives for a particular grain. For each grain it picks one of the available silos as a new host. The benefit of this approach is that the recovery process is performed only for grains that are actually being used and it is spread in time and across all available silos, which improves the responsiveness of the system and the speed of recovery. Note also that there is a delay between the time when a silo fails and when the Orleans cluster detects the failure. The delay is a configurable tradeoff between the speed of detection and the probability of false positives. During this transition period all calls to the grain will fail, but after the detection of the failure the grain will be created, upon a new call to it, on another silo, so it will be eventually available. More information can be found here.

如果一個grain調用需要太多的時間來執行,會發生什么?

 

 

Since Orleans uses a cooperative multi-tasking model, it will not preempt the execution of a grain automatically but Orleans generates warnings for long executing grain calls so you can detect them. Cooperative multi-tasking has a much better throughput compared to preemptive multi-tasking. You should keep in mind that grain calls should not execute any long running tasks like IO synchronously and should not block on other tasks to complete. All waiting should be done asynchronously using the await keyword or other asynchronous waiting mechanisms. Grains should return as soon as possible to let other grains execute for maximum throughput.

什么情況下分割用例(同時在多個silos中激活的同一個grain)?

This can never happen during normal operations and each grain will have one and only one instance per ID. The only time this can occur is when a silo crashes or if it’s killed without being allowed to properly shutdown. In that case there is a 30-60 seconds window (based on configuration) where a grain can exist in multiple silos before one is removed from the system. The mapping from grain IDs to their activation (instance) addresses is stored in a distributed directory (implemented with DHT) and each silo owns a partition of this directory. When membership views of two silos differ, both can request creation of an instance and as a result. two instances of the grain may co-exist. Once the cluster silos reach an agreement on membership, one of the instances will be deactivated and only one activation will survive. You can find out more about how Orleans manages the clusters at Cluster Management page. Also you can take a look at Orleans’s paper for a more detailed information, however you don’t need to understand it fully to be able to write your application code. You just need to consider the rare possibility of having two instances of an actor while writing your application.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM