Microsoft Orleans 之 入門指南

Microsoft Orleans

在.net用簡單方法構建高併發、分佈式的大型應用程序框架。html

原文:http://dotnet.github.io/orleans/前端

在線文檔:http://dotnet.github.io/orleans/What's-new-in-Orleansmysql

源碼地址:https://github.com/dotnet/orleansreact

簡介:Orleans 框架能夠構建大規模、高併發、分佈式應用程序,而不須要學習專業分佈式以及併發知識框架。它是由微軟研究和設計應用於雲計算。ios

Orleans 被普遍應用於微軟雲產品,值得注意的是微軟官方遊戲:Halo四、Halo5(光環|光暈)的雲服務所有由它來承載,以及愈來愈多的公司採用它。git

特色:1.默認的可拓展性,構建複雜的分佈式應用程序,可使您的程序輕易拓展到數百個服務。程序員

     2.低延時,它可使你的程序狀態保存於內存中,因此你的應用程序能夠快速響應請求。github

   3.簡化併發,Orleans 容許你使用C#代碼構建Actors 之間的異步處理消息。web

注:(Actor模型,請查看著名的Actor模型論文)算法

在Orleans,actors 被稱做grains,採用一個接口來表示,Actors的消息用異步方法來接受,以下:



運行在Orleans 框架裏的實現代碼public interface IMyGrain : IGrainWithStringKey { Task<string> SayHello(string name); }


而後經過建立代理對象,並調用Grains的方法來發送消息public class MyGrain : IMyGrain { public async Task<string> SayHello(string name) { return "Hello " + name; } }




接下來該作什麼呢?
若是要學習更多關於Orleans的概念,請閱讀 Orleans簡介
若是要學習入門教程請學習 一步一步教程var grain = GrainClient.GrainFactory.GetGrain<IMyGrain>("grain1"); await grain.SayHello("World");

 

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接口的類。
以下:



如今在服務器上,咱們應該有一個grain,發送這些聊天信息給客戶端。grain也應該有一個爲客戶端訂閱和退訂的消息機制。
訂閱的grain能夠用類ObserverSubscriptionManager:public class Chat : IChat { public void ReceiveMessage(string message) { Console.WriteLine(message); } }


可使用ObserverSubscriptionManager<IChat>的實例給客戶端發送通知消息,這個發送消息的方法接受一個Action<T>的委託或者一個表達式,您能夠調用接口上的任何方法,將其發送給客戶端。
在咱們的案例只有一個方法ReceiveMessage,咱們的服務器上的發送代碼會看起來像這樣: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); } }



如今咱們的服務器來發送信息觀察客戶的方法,用於訂閱/退訂和客戶端兩種方法實現了一個類可以觀察grain的消息。最後一步是建立基於咱們之前實現的聊天類客戶觀察員  引用 ,並讓它收它訂閱的消息。

代碼看起來像這樣:public Task SendUpdateMessage(string message) { _SubsManager.Notify(s => s.ReceiveMessage(message)); return TaskDone.Done; }Chat



如今咱們的服務器上調用grain的方法 ,全部訂閱的客戶端將收到消息。在咱們的客戶代碼, 的實例將變量c輸出到控制檯。
注意:支持觀察員可能會在將來的版本中刪除,取而代之的是一個簡單的消息流短信,它能夠支持相同的概念,更多的靈活性和可靠性。//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);SendUpdateMessageChat

常見問題:

微軟是否支持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失敗,會發生什麼?

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

你會收到一個異常,您能夠捕獲和重試或作任何事情在你的應用程序邏輯中有意義的。奧爾良運行時沒有當即從新建立從一個失敗的筒倉穀物由於他們不少人可能並不須要當即或在全部。相反,運行時單獨再現這種穀物,只有當一個新的請求到達特定的糧食。爲每一粒它選擇其中一個可用的筒倉做爲一個新的主機。這種方法的好處是,只能對穀物的實際使用執行恢復過程,它傳播的時空,跨越全部可用筒倉,從而提升了系統的響應和恢復的速度。還要注意是筒倉失敗時的時間之間有延遲和奧爾良羣集時檢測到故障。延遲時間是檢測的可配置速度和誤報的可能性之間的折衷。在此過渡期間對糧食的全部調用會都失敗,但後失敗的檢測糧食將會建立後一個新的呼籲,另外一個筒倉,, 因此纔會最終可用。能夠找到更多的信息在這裏

若是一個grain調用須要太多的時間來執行,會發生什麼?

 

 

因爲奧爾良使用合做多任務模型,它將不會自動搶佔一粒執行但奧爾良爲生成警告長執行糧食調用以便您能夠檢測到它們。合做多任務中有不少更好的吞吐量相比,搶佔式多任務。你應該牢記那粒電話不該該執行任何長時間運行的任務,像 IO 同步和不該阻止對要完成的其餘任務。全部的等待,應該是使用異步等待關鍵字或其餘異步等待機制。穀物應該儘快返回,讓其餘穀物執行的最大吞吐量。

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

這能夠永遠不會發生在正常操做過程當中,每一粒會有且僅有一個實例每 id。惟一的一次發生這種狀況是當筒倉崩潰時或若是被殺了不容許正確關機。在這種狀況下,還有一粒能夠存在多個筒倉中就有一個的窗口 30-60 秒 (根據配置) 是從系統中刪除。從糧食 Id 到其激活 (實例) 地址的映射存儲在分佈式目錄 (採用 DHT) 和每一個筒倉擁有此目錄分區。當會員各兩個筒倉持有不一樣意見時,均可以請求建立的實例和做爲一個結果。兩個實例的糧食可能並存。一旦羣集筒倉達成會員,其中一個實例將被停用,只有一個激活會生存。你能夠了解更多有關如何奧爾良管理集羣在羣集管理頁面。此外你能夠看看奧爾良的更詳細的信息,可是你不須要了解它徹底可以編寫您的應用程序代碼。你只被須要考慮的在編寫您的應用程序時有兩個實例的演員罕見的可能性。

 

先決條件

Orleans 是一個.net 類庫集,爲了使用它,你須要.net 4.5.1 或者更高版本,開發工具集須要visual studio 2015 或者更高版本或者其餘支持的開發工具,不支持Visual Studio的簡化版本或者拓展包,可是你能夠直接引用Orleans ,經過NuGet.

在生產環境中,Orleans 須要持久化存儲,目前只支持一下技術產品之一:

 

Azure 就很少說了微軟的雲平臺產品,固然就是要花錢買微軟的雲平臺、雲數據庫了

SQL Server 

Zookeeper 這又是個什麼東東呢,提及它那就得提到在大數據存儲、大數據運算下大名鼎鼎的Hadoop 了,它是屬於Hadoop項目下的一個子項目,用來存儲數據的,具體詳細你就問度娘吧。

MySQL 一個數據庫

Consul 這是什麼,不會吧,我孤陋寡聞了,歷來沒據說過這麼高達上的東西,好吧,問度娘:

Consul是HashiCorp公司推出的開源工具,用於實現分佈式系統的服務發現與配置。與其餘分佈式服務註冊與發現的方案,Consul的方案更"一站式",內置了服務註冊與發現框 架、分佈一致性協議實現、健康檢查、Key/Value存儲、多數據中心方案,再也不須要依賴其餘工具(好比ZooKeeper等)。使用起來也較 爲簡單。Consul用Golang實現,所以具備自然可移植性(支持Linux、windows和Mac OS X);安裝包僅包含一個可執行文件,方便部署,與Docker等輕量級容器可無縫配合。

注意:以上產品使用咱們會在後續章節中用示例的方式給你們展現,就期待吧。

 

Orleans Tools for Visual Studio

提及來也多虧visual studio 這個集大成的開發工具,作的那麼貼心,猶如毒品,難以戒掉:

正如一如既往的vs Orleans 也給咱們提供了模版化的項目建立,咱們只須要安裝這個vs 插件,而後就能夠在vs 中看到Orleans的項目模版了,

下載地址 Orleans Tools for Visual Studio

而後就是安裝了,若是成功安裝好了,啓動vs 就能夠看到以下結構

如上圖所示:1 是安裝好後Orleans 的一個模版集節點,選中後左邊面板中會有三個項目模版。

2 是咱們項目中一個服務的承載項目,這裏是用來測試用的,因此是一個控制檯應用程序,同時也將host所需的類庫nuget進來了。

針對你項目的須要,能夠是一個windows 服務,一個控制檯應用,一個Windows forms 的程序,或者也能夠寄宿到iis的web中。

3 是grain的實現 ,咱們大部分時間就是與裏面的東西打交道,它裏面是一些實現了grain藉口,或者業務類的一系列類集合。

4 是grain 接口定義處,爲什麼要孤立出來呢,由於未來這些接口是要暴露出來讓其餘須要的地方調用嗎。

好了,就介紹到這裏吧,你能夠去試試。

 

ETG.Orleans.Templates.VSIX

這個也是vs 插件,就很少囉嗦了,直接下載安裝就ok

Orleans NuGet Packages

orleans(1.2.2) 包

在大多數狀況下,有4個關鍵的NuGet包你須要使用:

Microsoft.Orleans.OrleansCodeGenerator.Build




grain 的接口或者實現項目提供構建支持,把他們添加到你的grain的接口和實現項目中能夠實現grain引用和序列化的代碼生成,
Microsoft.Orleans.Templates.Interfaces 和 Microsoft.Orleans.Templates.Grains 包已經廢棄,僅僅是爲了向後兼容而留用。

PM> Install-Package Microsoft.Orleans.OrleansCodeGenerator.Build

Microsoft.Orleans.Core



包含了Orleans.dll,定義了膽量的Orleans 的公共類型定義和客戶端的部分,引用他來構建你的類庫和客戶端項目,不須要包括更多的其餘提供者。

PM> Install-Package Microsoft.Orleans.Core

Microsoft.Orleans.Server


包含了你運行時須要的silos等的一些類型PM> Install-Package Microsoft.Orleans.Server

Microsoft.Orleans.Client




包括一切你客戶端須要的orleans 類型(前端)

額外的包

下面的包提供了額外的功能。PM> Install-Package Microsoft.Orleans.Client

Microsoft.Orleans.OrleansServiceBus



包含了雲事件流提供程序中心。PM> Install-Package Microsoft.Orleans.OrleansServiceBus

Microsoft.Orleans.OrleansHost



包含了默認silo 主機(OrleansHost.exe),可使用它來部署或者在雲工做項裏做爲一個宿主來部署,他包括了Microsoft.Orleans.Server

PM> Install-Package Microsoft.Orleans.OrleansHost

Microsoft.Orleans.OrleansAzureUtils




在 Azure Worker/Web roles 包含了silos 和 clients 的一組簡單的包裝類的實例,提供了基於Table的membership,雲存儲的持久化和stream

PM> Install-Package Microsoft.Orleans.OrleansAzureUtils

Microsoft.Orleans.OrleansProviders




包含一組內置的持久性和流provides ,包含在 Microsoft.Orleans.Client and Microsoft.Orleans.Server.

PM> Install-Package Microsoft.Orleans.OrleansProviders

Microsoft.Orleans.CounterControl



包含了OrleansCounterControl.exe (一個計數控制平臺),爲orleans註冊Windows性能計數器類別統計和部署grains類。能夠在雲端的一部分,做爲一個角色啓動任務。包括在PM> Install-Package Microsoft.Orleans.CounterControl
microsoft.orleans.server。

Microsoft.Orleans.OrleansManager




包括奧爾良管理工具OrleansManager.exe。

PM> Install-Package Microsoft.Orleans.OrleansManager

Microsoft.Orleans.OrleansConsulUtils


使用Consul爲集羣成員數據存儲提供的一個插件PM> Install-Package Microsoft.Orleans.OrleansConsulUtils

Microsoft.Orleans.OrleansZooKeeperUtils

PM> Install-Package Microsoft.Orleans.OrleansZooKeeperUtils
使用集羣成員數據存儲提供的一個插件ZooKeeper爲

Microsoft.Orleans.TestingHost


爲silos的主機測試項目提供的一個類庫

PM> Install-Package Microsoft.Orleans.TestingHost

Microsoft.Orleans.OrleansCodeGenerator



包括運行時代碼生成。包括在Microsoft.Orleans.Server和Microsoft.Orleans.Client.裏

PM> Install-Package Microsoft.Orleans.OrleansCodeGenerator

Microsoft.Orleans.OrleansTelemetryConsumers.AI



Includes the telemetry consumer for Azure Application Insights.
(不太清楚,多是對雲應用的一種測試吧)

PM> Install-Package Microsoft.Orleans.OrleansTelemetryConsumers.AI

Microsoft.Orleans.OrleansTelemetryConsumers.NewRelic



Includes the telemetry consumer for NewRelic.
同上

PM> Install-Package Microsoft.Orleans.OrleansTelemetryConsumers.NewRelic

Microsoft.Orleans.Serialization.Bond


提供了Bond serializer.序列化的一個支持

PM> Install-Package Microsoft.Orleans.Serialization.Bond

Bond 是一個擴展框架,用來處理系統化數據,特別適合用來處理與大數據存儲和處理服務的通信。

Bond 定義了一個豐富的類型系統和 schema 版本化規則,容許向前向後兼容。核心特性包括高性能序列化和反序列化,很是強大的通用數據傳輸機制。該框架是高可擴展性的,經過可插入式的序列化協議、數據流和用戶定義的類型別名等。

此外 Bond 是語言和平臺獨立的,當前支持 C++、C# 和 Python 語言。

namespace  Examples
{
     using  Bond;
     using  Bond.Protocols;
     using  Bond.IO.Safe;
 
     class  Program
     {
         static  void  Main()
         {
             var  src =  new  Example
             {
                 Name =  "FooBar" ,
                 Constants = { 3.14, 6.28 }
             };
 
             var  output =  new  OutputBuffer();
             var  writer =  new  CompactBinaryWriter<OutputBuffer>(output);
 
             // The first calls to Serialize.To and Deserialize<T>.From can take
             // a relatively long time because they generate the de/serializer 
             // for a given type and protocol.
             Serialize.To(writer, src);
 
             var  input =  new  InputBuffer(output.Data);
             var  reader =  new  CompactBinaryReader<InputBuffer>(input);
 
             var  dst = Deserialize<Example>.From(reader);
         }
     }
}
 

 

開發一個Grain 

在開發Grain以前請先閱讀Grains 這篇文章

Grain 接口

Grains經過調用各自定義在接口中的方法相互做用,一個grain實現了事先定義好的一個或多個接口,grain接口中的方法必須返回Task(若是沒有返回值) 或者Task<T>(若是有類型返回值)

以下事例樣本:



Grain 引用
一個grain引用是一個代理對象,實現了相同grain接口由相應的grain類實現。使用異步消息,grains之間實現全雙工通信,以及和客戶端,grain引用是經過grain的identity並經過調用方法GrainFactory.GetGrain<T>()
來建立,開發人員使用它就像使用普通.net 對象同樣,它能夠做爲一個方法的返回值傳遞一個方法,以下:
在orleans 客戶端代碼://an example of a Grain Interface public interface IPlayerGrain : IGrainWithGuidKey { Task<IGameGrain> GetCurrentGame(); Task JoinGame(IGameGrain game); Task LeaveGame(IGameGrain game); } //an example of a Grain class implementing a Grain Interface public class PlayerGrain : Grain, IPlayerGrain { private IGameGrain currentGame; // Game the player is currently in. May be null. public Task<IGameGrain> GetCurrentGame() { return Task.FromResult(currentGame); } // Game grain calls this method to notify that the player has joined the game. public Task JoinGame(IGameGrain game) { currentGame = game; Console.WriteLine("Player {0} joined game {1}", this.GetPrimaryKey(), game.GetPrimaryKey()); return TaskDone.Done; } // Game grain calls this method to notify that the player has left the game. public Task LeaveGame(IGameGrain game) { currentGame = null; Console.WriteLine("Player {0} left game {1}", this.GetPrimaryKey(), game.GetPrimaryKey()); return TaskDone.Done; } }

在另一個grain中使用://construct the grain reference of a specific player IPlayerGrain player = GrainClient.GrainFactory.GetGrain<IPlayerGrain>(playerId);


Grain方法調用

Orleans編程模型是一組異步編程模型,可使用async and await,更詳細的文章請看這裏
使用前面的例子來使用這個對象,代碼以下://construct the grain reference of a specific player IPlayerGrain player = GrainFactory.GetGrain<IPlayerGrain>(playerId);


也能夠鏈接兩個或者兩個以上的Task 而後組合一個Task來完成任務,這是一種比較有用的方式,等待全部的任務完成而後在統計,以下實例://Invoking a grain method asynchronously Task joinGameTask = player.JoinGame(this); //The `await` keyword effectively turns the remainder of the method into a closure that will asynchronously execute upon completion of the Task being awaited without blocking the executing thread. await joinGameTask; //The next lines will be turned into a closure by the C# compiler. players.Add(playerId);




List<Task> tasks = new List<Task>(); ChirperMessage chirp = CreateNewChirpMessage(text); foreach (IChirperSubscriber subscriber in Followers.Values) { tasks.Add(subscriber.NewChirpAsync(chirp)); } Task joinedTask = Task.WhenAll(tasks); await joinedTask;

TaskDone.Done Utility Property

有沒有一個標準的方式返回一個void的返回值呢,答案是確定的,Orleans 爲咱們定義一個助手類:TaskDone.Done

 

客戶端開發

一旦咱們定義好一個grain的接口而且有相應的實現類,咱們就能夠開始編寫咱們的客戶端代碼,引入相應的dll

  • Orleans.dll
  • OrleansRuntimeInterfaces.dll

幾乎任何一個客戶端都會涉及到grain 工廠方法的使用,使用這個方法經過一個特殊的Id來引用,正如已經提到的grain不能被顯式建立或者銷燬。




若是這個應用程序代碼放在控制檯應用程序的主線程中的話,你就須要調用wait()方法。
查看更多細節的關鍵概念部分關於使用任務執行調度和異常流的詳細信息。

查找或者建立一個Grains
經過調用GrainClient.Initialize()創建鏈接以後,在泛型工廠類中的靜態方法能夠用於獲取一個grain的引用,例如:GrainClient.GrainFactory.GetGrain<IPlayerGrain>()
Grain接口做爲GrainFactory.GetGrain<T>()的一個參數來傳遞。
給Grains發送消息
在orleans 的編程模型中客戶端與服務端的通信相似於grain之間的通信,區別是client的調用是多是多線程調用,而grain之間被約束爲單線程,客戶端庫使用TPL的線程池管理的延續和回調,
因此它是由客戶端來管理本身的併發,使用任何同步構建適合其環境–鎖、事件、TPL 任務,等等。
獲取通知
有一種狀況,其中一個簡單的消息/響應模式是不夠的,客戶端須要接收異步通知。例如,一個用戶可能但願被通知當一個新的消息已發表的人。
觀察者是一個單向異步接口繼承自IGrainObserver,它的方法必須返回void,觀察者調用一個grain的接口方法就是發出一個消息通知,除了沒有返回值外,grain不依賴於任何結果,
Orleans將確保消息單向傳遞的,grain提供了相應訂閱/發佈通知的API
訂閱通知必須首先建立一個實現了觀察者接口的對象,而後在grain factory裏調用CreateObjectReference方法,轉向grain對象的引用,而後能夠將其傳遞給通知grain的訂閱方法。
該模型也能夠被其餘grain用於接收異步通知。不像在客戶端訂閱的狀況,訂閱grain只是實現Observer接口做爲一個面,並經過對自身的引用 (e.g. ).
Example
這裏是一個完整代碼的例子GrainClient.Initialize(); // Hardcoded player ID Guid playerId = new Guid("{2349992C-860A-4EDA-9590-000000000006}"); IPlayerGrain player = GrainClient.GrainFactory.GetGrain<IPlayerGrain>(playerId); IGameGrain game = player.CurrentGame.Result; var watcher = new GameObserver(); var observer = GrainClient.GrainFactory.CreateObjectReference<IGameObserver>(watcher); await game.SubscribeForGameUpdates();this.AsReference<IChirperViewer>
namespace PlayerWatcher { class Program { /// <summary> /// Simulates a companion application that connects to the game /// that a particular player is currently part of, and subscribes /// to receive live notifications about its progress. /// </summary> static void Main(string[] args) { try { GrainClient.Initialize(); // Hardcoded player ID Guid playerId = new Guid("{2349992C-860A-4EDA-9590-000000000006}"); IPlayerGrain player = GrainClient.GrainFactory.GetGrain<IPlayerGrain>(playerId); IGameGrain game = null; while (game == null) { Console.WriteLine("Getting current game for player {0}...", playerId); try { game = player.CurrentGame.Result; if (game == null) // Wait until the player joins a game Thread.Sleep(5000); } catch (Exception exc) { Console.WriteLine("Exception: ", exc.GetBaseException()); } } Console.WriteLine("Subscribing to updates for game {0}...", game.GetPrimaryKey()); // Subscribe for updates var watcher = new GameObserver(); game.SubscribeForGameUpdates(GrainClient.GrainFactory.CreateObjectReference<IGameObserver>(watcher)).Wait(); // .Wait will block main thread so that the process doesn't exit. // Updates arrive on thread pool threads. Console.WriteLine("Subscribed successfully. Press <Enter> to stop."); Console.ReadLine(); } catch (Exception exc) { Console.WriteLine("Unexpected Error: {0}", exc.GetBaseException()); } } /// <summary> /// Observer class that implements the observer interface. /// Need to pass a grain reference to an instance of this class to subscribe for updates. /// </summary> private class GameObserver : IGameObserver { // Receive updates public void UpdateGameScore(string score) { Console.WriteLine("New game score: {0}", score); } } } }
 
運行應用程序
配置鏈接到Orleans
容許應用程序與外界交互的orleans grains,框架包括客戶端庫。此客戶端程序庫可能由桌面或移動應用程序使用,或由呈現交互式網頁或公開Web服務接口的前端服務器使用。
客戶端庫提供了一個API編寫異步客戶端於orleans交互。一旦客戶端庫鏈接到orleans的網關,客戶端能夠發送郵件到grains,接收響應,經過觀察者獲得grain異步通知。


鏈接網關
創建一個鏈接,客戶端經過調用GrainClient.Initialize(),他將鏈接到silo在配置文件(ClientConfiguration.xml )中指定的ip和端口,這個文件必須和
Orleans.dll放在同一個目錄下,做爲一種替代,你也能夠經過編程的方式加載一個配置文件而後經過 初始化來使用。
配置客戶端
在客戶端配置文件中(ClientConfiguration.xml),指定網關端點的ip和端口,它須要去匹配silo中配置文件(OrleansConfiguration.xml )中指定的網關GrainClient.Initialize()


若是Orleans運行在Windows Azure,客戶端就會自動發現網關信息,而就不須要靜態配置了,能夠參考這個實例 Azure application sample 

配置silos
在OrleansConfiguration.xml配置文件中,ProxyingGateway代理網關元素指定了silo的endpoint網關(gateway),inter-silo的地址定義是經過Networking元素來定義的,必須區分開
且要跟它指定不一樣的端口。
<ClientConfiguration xmlns="urn:orleans"> <Gateway Address="<IP address or host name of silo>" Port="30000" /> </ClientConfiguration>
<?xml version="1.0" encoding="utf-8"?> <OrleansConfiguration xmlns="urn:orleans"> <Defaults> <Networking Address="" Port="11111" /> <ProxyingGateway Address="" Port="30000" /> </Defaults> </OrleansConfiguration>
grain 持久化
grain持久化目標
1.容許不一樣的grain類型使用不一樣的存儲類型providers(一個用Azure 表,一個用SQL Azure )相同類型的存儲提供程序,但具備不一樣的配置(兩個使用Azure表,但一個使用存儲賬戶)。
2.容許存儲提供程序實例配置可交換(例如,開發-測試-產品)只是配置文件的變化。
3.容許額外的存儲供應商被寫入後提供了一個框架,由orleans團隊或其餘人。
4.提供一個最小的生產級存儲提供程序。
5.存儲提供程序徹底控制了在持久化支持存儲中的數據狀態數據的存儲方式。
推論:Orleans沒有提供一個全面的ORM的存儲解決方案,但允在必要的時候許自定義存儲提供程序支持指定一個ORM的要求。
grain 持久化API
Grain類型能夠用兩種方式中的一種聲明:
1.拓展Grain 若是他們沒有任何持久的狀態,或若是他們將處理全部本身的持久狀態,或者
2.Extend 另外,經過拓展Grain<T> grain類型自動選擇Orleans框架的系統管理的持久化。

對於本節的其餘部分,咱們只會考慮選擇 #2 / Grain 狀態存儲
從Grain<T>繼承的grain類(其中T是從GrainState自動導出的存儲數據類型)將從指定的存儲中自動加載它們的狀態。

Grain將標有[StorageProvider]特性指定用於讀/寫Grain狀態的數據存儲提供程序實例的命名。Grain<T> 若是他們有一些持久態,他們但願Orleans良運行時處理。
Grain<T> ,由於條件限制。



Orleans Provider 管理框架提供了一種機制,以指定和註冊silo配置文件不一樣的存儲提供和存儲操做。[StorageProvider(ProviderName="store1")] public class MyGrain<MyGrainState> ... { ... }



<OrleansConfiguration xmlns="urn:orleans"> <Globals> <StorageProviders> <Provider Type="Orleans.Storage.MemoryStorage" Name="DevStore" /> <Provider Type="Orleans.Storage.AzureTableStorage" Name="store1" DataConnectionString="DefaultEndpointsProtocol=https;AccountName=data1;AccountKey=SOMETHING1" /> <Provider Type="Orleans.Storage.AzureBlobStorage" Name="store2" DataConnectionString="DefaultEndpointsProtocol=https;AccountName=data2;AccountKey=SOMETHING2" /> </StorageProviders>
配置存儲providers
AzureTableStorage



如下屬性能夠添加到<Provider / >元素來配置提供者:<Provider Type="Orleans.Storage.AzureTableStorage" Name="TableStore" DataConnectionString="UseDevelopmentStorage=true" />
  • DataConnectionString="..." (強制) - Azure 存儲鏈接字符串。
  • TableName="OrleansGrainState" (可選) - 表存儲中使用的表名稱,默認OrleansGrainState
  • DeleteStateOnClear="false" (可選) - 若是是true,當清除grain時記錄被刪除,不然將寫入一個空記錄,默認爲false
  • UseJsonFormat="false" (可選) - 若是爲true, 使用json序列化, 使用Orleans的二進制序列化, 默認 false
  • UseFullAssemblyNames="false" (可選) - (若是 UseJsonFormat="true") 若是爲true則使用程序集全名,不然使用簡單名, 默認爲 false
  • IndentJSON="false" (可選) - (若是 UseJsonFormat="true") 若是爲true壓縮json序列化, 默認false

 

注:表存儲限制,state不該該超過64kb

 

AzureBlobStorage



如下屬性能夠添加到</>元素來配置提供者:<Provider Type="Orleans.Storage.AzureTableStorage" Name="BlobStore" DataConnectionString="UseDevelopmentStorage=true" />Provider
  • DataConnectionString="..." (強制) - Azure 存儲鏈接字符串。
  • ContainerName="grainstate" (可選) - 使用blob 存儲容器, 默認 grainstate
  • UseFullAssemblyNames="false" (可選) - (若是 UseJsonFormat="true") 若是爲true則使用程序集全名,不然使用簡單名, 默認爲 false
  • IndentJSON="false" (可選) - 若是爲true壓縮json序列化, 默認false

MemoryStorage



注:provider 持久狀態保存到內存中,若是silo關閉就不復存在,因此僅工測試開發時使用。<Provider Type="Orleans.Storage.MemoryStorage" Name="MemoryStorage" />
  • NumStorageGrains="10" (可選) -用於存儲」狀態「的grain數, 默認 10

ShardedStorageProvider




用於寫入在多個其餘存儲提供程序中共享的Grain狀態數據的簡單存儲提供程序。

一致的散列函數(默認Jenkins Hash)來決定哪些碎片(按它們在配置文件中定義的順序)是負責爲指定的grain存儲狀態數據,而後經過適當的底層provider執行 讀/寫/清除請求
注:Jenkins Hash算法 是一種算法,若是想要了解更多去網上搜索。

存儲provider註解

若是繼承了Grain<T>的grain的類沒有指定 特性,那麼就搜索默認的provider,若是沒有找到,那麼這將被視爲一個丟棄的存儲provider。
若是在silo 配置文件中只有一個provider,它將被視爲這個silo的默認provider。
一個了使用不存在可是在silo配置中定義的存儲provider的grain,當silo加載器加載失敗時,silo的其餘部分仍然會加載,那麼之後當調用這個grain時,將會拋出Orleans.Storage.BadProviderConfigException異常,指定這個grain沒法加載。
存儲provider實例使用一個給定的grain類型是由在grain類型的[ StorageProvider]特性定義的存儲提供程序名稱的組合決定的,再加上provider的類型和在silo配置中肯定肯定的provider配置選項。
不一樣的grain類型可使用不一樣的配置存儲provider,即便兩個都是同一類型的:例如:兩種不一樣的Azure 表存儲提provider的實例,鏈接到不一樣的Azure存儲帳號(請參見上面的配置文件示例)。

存儲provider的全部配置細節都是靜態定義的,在silo啓動時讀取到silo結構中,在這個時候沒有什麼機制能夠動態地更新或改變一個silo使用的存儲provider的列表。
然而,這是一個優先次序/工做負載的約束,而不是一個基本的設計約束。

State 存儲 APIS

grain 有兩個主要的部分 state / persistence APIs: Grain-to-Runtime and Runtime-to-Storage-Provider.

Grain State存儲API
在Orleans運行狀態存儲功能將爲grain提供grain的讀寫操做和自動填充/保存 grain的GrainState數據對象。
在後臺,經過適當的持久化provider配置將爲grain提供這些功能鏈接(由Orleans客戶創工具在生成的代碼)。

Grain 狀態的讀寫功能
當Grain激活時,它的狀態將自動讀取,可是,grain是負責明確地觸發寫入任何改變的晶粒狀態,在必要時。
參見下面的錯誤處理機制的詳細信息的故障模式部分(Failure Modes)。
在OnActivateAsync()(等同於base.ReadStateAsync()的調用)方法的調用時將激活 的自動讀取,在任何調用grain的方法以前 的狀態是不會刷新,除非是,grain 激活後的這個調用。
任何grain的方法調用時,能夠要求Orleans運行時寫當前grain的狀態數據,激活指定的存儲provider經過調用writestateasync()。當他們對狀態數據進行重大更新時,grain負責顯式地執行寫操做。
一般,grain的方法將返回base.writestateasync() Task 從grain方法返回結果Task 做爲最終的結果,但它並不須要遵循這個模式。在任何grain方法後,運行時不會自動更新儲存的grain的狀態。
在grain的任何一種方法或計時器回調處理程序中,grain能夠request奧爾良運行時從新讀取當前grain 狀態數據,經過調用base.readstateasync()激活從指定的存儲provider。
這將徹底覆蓋當前存儲在當前狀態對象中的任何當前狀態數據,這些數據是由持久存儲的最新值讀取的。
(這句無法翻譯了,直接上原文)
An opaque provider-specific  value () may be set by a storage provider as part of the grain state metadata populated when state was read. Some providers may choose to leave this as  if they do not uses.

從概念上講,<Provider Type="Orleans.Storage.ShardedStorageProvider" Name="ShardedStorage"> <Provider /> <Provider /> <Provider /> </Provider>[StorageProvider]GrainStateGrainStateEtagstringnullEtag
任何寫操做期間Orleans運行時將以grain狀態數據對象的深層副本供本身使用。在重寫的狀況下,運行時可使用優化規則和啓發式,以免在某些狀況下,在某些狀況下執行一些或全部的深拷貝,提供預期的邏輯隔離語義被保留。

Grain狀態的讀寫操做的示例代碼
爲了加入Orleans的grain狀態持久性機制,必須拓展Grain類。
上述定義中的T將被此grain的特定應用程序特定的grain狀態類所取代;見下面的示例。
grain類也應該有一個[ storageprovider ]特性告訴運行時存儲provider(實例)使用這種類型的grain。



public interface MyGrainState : GrainState { public int Field1 { get; set; } public string Field2 { get; set; } } [StorageProvider(ProviderName="store1")] public class MyPersistenceGrain : Grain<MyGrainState>, IMyPersistenceGrain { ... }

Grain State Read

在grain的OnActivateAsync()方法調用以前,grain 的讀取狀態將會自動發生,沒有應用程序代碼使使這種狀況發生是必需的。要查看這個狀態,能夠經過grain 類的Grain<T>.State屬性。

Grain State Write

在對grain的內存狀態做出適當的改變,grain應該調用base.writestateasync()方法將更改寫入持久存儲,經過定義在存儲provider的grain。此方法是異步的,並返回一個Task。




public Task DoWrite(int val) { State.Field1 = val; return base.WriteStateAsync(); }

Grain State Refresh

若是一個grain但願顯式地從新從provider讀取這個grain的最新狀態,grain 應該調用base.ReadStateAsync() 方法,這將從新加載grain的狀態從持久存儲裏,經過定義的存儲provider的grain類型,在grain狀態存儲器複製以往任何將被覆蓋和取代base.readstateasync()任務完成時。

 




grain狀態持久化操做的失效模式
grain狀態讀取操做的失效模式

在初始讀取該特定grain的狀態數據時,存儲提供程序返回的故障將致使該grain的激活操做失敗;既然這樣,沒有必要再去調用grain的OnActivateAsync()在生命週期內,在grain激活過程當中,因爲任何其餘故障,將原始請求原路返回。
常見的存儲provider爲一個特定的grain讀取狀態數據會致使 。grain能夠選擇處理或忽略,失敗的Task,在Orleans就像任何其餘的Task。
任何試圖在加載silo期間沒有正確加載provider的框架內的grain發送消息,都將會收到一個異常Orleans.BadProviderConfigException

grain狀態寫入操做的故障模式
Failures encountered by the storage provider to write state data for a particular grain will result in the  to be faulted. Usually,
this will mean the grain call will be faulted back to the client caller provided the   is correctly chained in to the final return  for this grain method. However,
it will be possible for certain advanced scenarios to write grain code to specifically handle such write errors,
just like they can handle any other faulted .

Grains that execute error-handling / recovery code must catch exceptions / faulted s and not re-throw to
signify that they have successfully handled the write error.

注意:此段對於程序中的細節處理很是重要


public async Task<int> DoRead() { await base.ReadStateAsync(); return State.Field1; }ReadStateAsync()Task 調用失敗WriteStateAsync()TaskWriteStateAsync()TaskTaskTaskWriteStateAsync()Task

Storage Provider Framework

有編寫額外的持久化provider的服務提供程序接口 – IStorageProvider。持久性提供程序API包括讀和寫操做grainstate數據。




public interface IStorageProvider { Logger Log { get; } Task Init(); Task Close(); Task ReadStateAsync(string grainType, GrainId grainId, GrainState grainState); Task WriteStateAsync(string grainType, GrainId grainId, GrainState grainState); }

Storage Provider Semantics

任何試圖執行寫操做時,存儲provider檢測ETag違反約束致使寫的Task被終端,就使用Orleans.InconsistentStateException包裝底層存儲異常。

public class InconsistentStateException : AggregateException { /// <summary>The Etag value currently held in persistent storage.</summary> public string StoredEtag { get; private set; } /// <summary>The Etag value currently held in memory, and attempting to be updated.</summary> public string CurrentEtag { get; private set; } public InconsistentStateException( string errorMsg, string storedEtag, string currentEtag, Exception storageException ) : base(errorMsg, storageException) { this.StoredEtag = storedEtag; this.CurrentEtag = currentEtag; } public InconsistentStateException(string storedEtag, string currentEtag, Exception storageException) : this(storageException.Message, storedEtag, currentEtag, storageException) { } }


Data Mapping

我的存儲provider必須決定如何最好地儲存grain的狀態–BLOB(各類格式/序列化格式)或列每場是顯而易見的選擇。

對於table的基本存儲provider編碼狀態數據字段到使用orlenas二進制序列化單表列。



Application Bootstrapping within a Silo

當silos上線在應用程序須要運行一些「自動執行」功能的幾種狀況。

咱們如今已經增長了支持這種自動運行功能經過配置「orleans silo provider」。例如:




它也能夠註冊programaticaly引導供provider,經過調用:<OrleansConfiguration xmlns="urn:orleans"> <Globals> <BootstrapProviders> <Provider Type="My.App.BootstrapClass1" Name="bootstrap1" /> <Provider Type="My.App.BootstrapClass2" Name="bootstrap2" /> </BootstrapProviders> </Globals> </OrleansConfiguration>


 class.

這個啓動項provider 是實現了Orleans.Providers.IBootstrapProvider接口
當silo 啓動時,Orleans 運行時就是實例化bootstrap 類列表,在適當的運行上下文執行時調用他們的init方法,做爲一個客戶端發送消息到grainspublic void RegisterBootstrapProvider(string providerTypeFullName, string providerName, IDictionary<string, string> properties = null) public void RegisterBootstrapProvider<T>(string providerName, IDictionary<string, string> properties = null) where T : IBootstrapProvider Orleans.Runtime.Configuration.GlobalConfiguration



例外狀況是,當silo啓動調用silo拋出異常時,這個silo就會中止。

This fail-fast approach is the standard way that Orleans handles silo start-up issues,
and is intended to allow any problems with silo configuration and/or bootstrap logic to be easily
detected during testing phases rather than being silently ignored and causing unexpected problems later in the silo lifecycle.


定時器和提醒(Timers and reminders)

Orleans 運行時提供了兩個機制:定時器和提醒的功能,容許grain週期性的執行某一個行爲。

Task Init( string name, IProviderRuntime providerRuntime, IProviderConfiguration config)

Timers

描述:定時器是用來建立週期性grain的行爲,不須要跨多個激活的grain實例。它本質上是一組.NET System.Threading.Timer 類,另外它是受單線程執行保護的grain激活。

每一個激活可能有與它相關聯的零或更多的計時器,運行時在激活的運行時上下文中執行每一個定時程序。

用法:開始一個定時器,要使用 Grain.RegisterTimer 方法, 返回一個實現 IDisposable 引用的實例



asyncCallback 當定時器到執行時間的時候調用的一個函數或者功能。protected IDisposable RegisterTimer(Func<object, Task> asyncCallback, object state, TimeSpan dueTime, TimeSpan period)
asyncCallback 時傳入的一個對象
dueTime 指定定時器開始執行時第一次執行的等待時間。
period 指定定時器執行的時間間隔


取消定時器的處理:當激活被deactivated 或當一個故障發生時,一個定時器將中止觸發,而且它的silo崩潰。

重要的考慮因素:state 當執行
  • When activation collection is enabled, the execution of a timer callback does not change the activation’s state from idle to in use. This means that a timer cannot be used to postpone deactivation of otherwise idle activations.
  • The period passed to Grain.RegisterTimer is the amount of time that passes from the moment the Task returned by asyncCallback is resolved to the moment that the next invocation of asyncCallback should occur. This not only makes it impossible for successive calls to asyncCallback to overlap but also makes it so that the length of time asyncCallback takes to complete affects the frequency at whichasyncCallback is invoked. This is an important deviation from the semantics of System.Threading.Timer.
  • Each invocation of asyncCallback is delivered to an activation on a separate turn and will never run concurrently with other turns on the same activation. Note however, asyncCallback invocations are not delivered as messages and are thus not subject to message interleaving semantics. This means that invocations of asyncCallback should be considered to behave as if running on a reentrant grain with respect to other messages to that grain.


Reminders

提醒相似於定時器,可是有幾個重要的區別

描述:

使用:

  • 提醒持續存在,並將繼續觸發在全部狀況下(包括部分或所有集羣從新啓動)除非明確取消。
  • 提醒與一個grain,沒有任何特定的激活.
  • If a grain has no activation associated with it and a reminder ticks, one will be created. e.g.: If an activation becomes idle and is deactivated, a reminder associated with the same grain will reactivate the grain when it ticks next.
  • Reminders are delivered by message and are subject to the same interleaving semantics as all other grain methods.
  • 提醒不該該被用於高頻定時器,它們的週期應該在幾分鐘,幾小時或幾天內測量。

 

Configuration

 提醒,是持久的,依賴於存儲到功能。您必須指定在提醒子系統功能以前使用的存儲支持。提醒功能是經過在服務器端配置的systemstore元控制。它與Azure Table 或 SQL Server 存儲一塊兒協做。




若是你只是想提醒佔個位置來運做,而不須要創建一個Azure賬戶或SQL數據庫,那麼添加此元素的配置文件(「Globals」)會給你一個發展的惟一實現的提醒系統:<SystemStore SystemStoreType="AzureTable" /> OR <SystemStore SystemStoreType="SqlServer" />
使用:若是一個grain要使用提醒功能就必須實現接口IRemindable內的方法RecieveReminder <ReminderService ReminderServiceType="ReminderTableGrain"/>



去啓動一個提醒功能,使用Grain.RegisterOrUpdateReminder方法,而後他會返回IOrleansReminder的一個對象Task IRemindable.ReceiveReminder(string reminderName, TickStatus status) { Console.WriteLine("Thanks for reminding me-- I almost forgot!"); return TaskDone.Done; }
protected Task<IOrleansReminder> RegisterOrUpdateReminder(string reminderName, TimeSpan dueTime, TimeSpan period)
  • reminderName 是一個惟一的標識,在grain上下文範圍內。
  • dueTime 指定第一次提醒多久開始執行。
  • period 指定執行間隔。

因爲激活的生命週期就是grain惟一的生命週期,因此你必須明確的取消,使用Grain.UnregisterReminder:




 Grain.RegisterOrUpdateReminder.方法的調用會返回提醒操做handler

若是你想在一個持續的方式識別一個提醒,使用一個包含提醒的名稱的字符串的IOrleansReminder 實例不能保證有效期超出一個激活的壽命。
若是你只是想經過提醒的name來的到一個提醒實例的話,調用Grain.GetReminder方法。protected Task UnregisterReminder(IOrleansReminder reminder)


咱們該用那個一個功能呢?
1.在下面幾種狀況使用定時器protected Task<IOrleansReminder> GetReminder(string reminderName)
  • It doesn’t matter (or is desirable) that the timer ceases to function if the activation is deactivated or failures occur.
  • If the resolution of the timer is small (e.g. reasonably expressible in seconds or minutes).
  • The timer callback can be started from Grain.OnActivateAsync or when a grain method is invoked.
2.這幾種狀況下使用提醒功能
  • When the periodic behavior needs to survive the activation and any failures.
  • To perform infrequent tasks (e.g. reasonably expressible in minutes, hours, or days).
組合提醒和定時器
You might consider using a combination of reminders and timers to accomplish your goal.
For example, if you need a timer with a small resolution that needs to survive across activations,
you can use a reminder that runs every five minutes, whose purpose is to wake up
a grain that restarts a local timer that may have been lost due to a deactivation.



 

Orleans Streams

Orleans v.1.0.0添加流擴展的編程模型支持,流擴展提供了一組抽象接口api,使它的流更簡單和更強大的,流擴展容許開發人員以結構化的方式寫操做在一系列事件上的應用程序。

流provider的可擴展性模型使得在普遍的現有的隊列技術的編程模型兼容和方便,例如: Event Hubs,ServiceBusAzure Queues, 和 Apache Kafka. 而沒必要要寫特殊代碼或運行專用程序與這樣的隊列進行交互。

我爲何要關心?

若是你已經瞭解了一些關於流處理的技術如:Event HubsKafka,Azure Stream AnalyticsApache StormApache Spark Streaming, 和Reactive Extensions (Rx) 

你可能會問你爲何要關心,爲何咱們須要另外一個流處理系統和Actor是如何相關的流?「Why Orleans Streams?」能夠回答你的問題

編程模型

下面是一些Orleans 流編程模型原則:

  1. 在Orleans 的體系裏 Orleans virtual actors, Orleans 流是虛擬的,也就是說,一個流老是存在的。它不是顯式建立或銷燬,它永遠不會失敗。
  2. 流的身份ID識別,這只是邏輯名稱由GUID字符串表示。
  3. Orleans 流數據的生成將容許從時間和空間上減弱它們的依賴性。這意味着流的生產者和流的消費者可能會在不一樣的服務器上,在不一樣的時間,並將承受失敗。
  4. Orleans 的流是輕量級和動態的,Orleans 流運行時的設計是處理高速大數據。
  5. Orleans 流是動態綁定的,Orleans 流運行時的設計處理的狀況下,grain鏈接和斷開流在一個較高的速度。
  6. Orleans 流運行時透明地管理流消費的生命週期。當應用程序訂閱一個流,而後它將接收流的事件,即便是在出現故障。
  7. Orleans 流均勻分佈在grain和 工做的客戶端。

編程APIs

應用程序流訪問APIs相似於衆所周知的 Reactive Extensions (Rx) in .NET,經過使用  實現了 和  的接口。
在下面的一個典型例子產生的一些數據,這是一個HTTP請求的服務在雲中運行的請求。Orleans客戶端運行在前端服務器接收HTTP調用和發佈數據流匹配裝置:Orleans.Streams.IAsyncStream<T>Orleans.Streams.IAsyncObserver<T>Orleans.Streams.IAsyncObservable<T>




另外一個例子是在聊天的用戶(如Orleans 的grain)實施加入聊天室,獲得一個處理流的聊天信息在這房間裏的全部其餘用戶產生和訂閱它。注意,聊天用戶既不須要知道聊天室的grain自己(可能不會有這樣的grain在咱們的系統中),也不是在該組的其餘用戶產生消息。不用說,對產生的聊天流,用戶不須要知道誰是目前訂閱的流。這演示瞭如何聊天用戶能夠徹底解耦的時間和空間。

public async Task OnHttpCall(DeviceEvent deviceEvent) { // Post data directly into device's stream. IStreamProvider streamProvider = GrainClient.GetStreamProvider("myStreamProvider"); IAsyncStream<DeviceEventData> deviceStream = streamProvider.GetStream<DeviceEventData>(deviceEvent.DeviceId); await chatStream.OnNextAsync(deviceEvent.Data); }




快速入門示例

Quick Start Sample (這裏是一個演示的例子),Streams Programming APIs (流編程模型APIs)

public class ChatUser: Grain { public async Task JoinChat(string chatGroupName) { IStreamProvider streamProvider = base.GetStreamProvider("myStreamProvider"); IAsyncStream<string> chatStream = streamProvider.GetStream<string>(chatGroupName); await chatStream.SubscribeAsync((string chatEvent) => Console.Out.Write(chatEvent)); } }

Stream Providers

流能夠經過各類形狀和形式的物理信道,能夠有不一樣的語義。

Orleans 流的設計是支持多種Stream Providers的概念,這是系統中的一個可拓展點,Orleans 目前提供兩種Stream providers。

基本的Tcp 通訊Simple Message Stream Provider和雲隊列Azure Queue Stream Provider,你能夠在這裏(Stream Providers)找到更消息的介紹

Stream 意義

Stream Subsription Semantics: Orleans Streams guarantee Sequential Consistency for Stream Subsription operations. Specificaly, when consumer subscribes to a stream, once the Task representing the subsription operation was successfuly resolved, the consumer will see all events that were generated after it has subscribed. In addition, Rewindable streams allow to subscribe from an arbitrary point in time in the past by using StreamSequenceToken (more details can be found here).

Individual Stream Events Delivery Guarantees: Individual event delivery guarantees depend on individual stream providers. Some provide only best-effort at-most-once delivery (such as Simple Message Streams), while others provide at-least-once delivery (such as Azure Queue Streams). It is even possible to build a stream provider that will guarantee exactly-once delivery (we don’t have such a provider yet, but it is possible to build one with the extensability model).

Events Delivery Order: Event order also depends on a particular stream provider. In SMS streams, the producer explicitelly controls the order of events seen by the consumer by controlling the way it publishes them. Azure Queue streams do not guarantee FIFO order, since the underlaying Azure Queues do not guarantee order in failure cases. Applications can also control their own stream delivery ordering, by usingStreamSequenceToken.

 

流實施

 Orleans Streams Implementation提供了一個高層次的內部實施概述。

Streams 的可拓展性

Orleans Streams Extensibility 描述如何用新的功能擴展流。

Code Samples

更多的例子:here.
更多的資料:

 

調試符號

在開發期間Orleans調試比較簡單,直接附加進程,可是對於在生產環境來講,就沒法斷點調試了,採用跟蹤是最好的辦法。

標記(Symbols):

Symbols for Orleans binaries are published to https://nuget.smbsrc.net symbols server. Add it to the list of symbols server in the Visual Studio options under Debugging/Symbols for debugging Orleans code. Make sure there is traling slash in the URL. Visual Studio 2015 has a bug with parsing it.

 

Orleans之Hello World

 

接觸Orleans 有一段時間了,以前也翻譯了一系列官網文檔,今天咱們就來一個實際的例子,來看看到底如何用這個東西來開發項目,固然經典的也是醉人的,咱們就從HelloWorld開始吧。

經過前面的知識準備咱們知道Orleans 項目須要n個服務端(就是silohost),n個客戶端(就是調用方),而後就是提供的actors(在Orleans 中成爲grain),廢話少說。

首先創建一個解決方案,叫作OrleansSamples

而後,增長一個模塊解決方案,叫作HelloWorlds,在解決方案下增長兩個類庫Sample.Interfaces,Sample.Implements,其中Sample.Implements引用Sample.Interfaces,

這兩個項目中引用Orleans的核心庫,你能夠手動一個一個引用進來,但仍是老老實實的用nuget吧。

nuget的引用兩種方式一種,經過圖形化的方式,另外一種經過命令的方式,命令:Install-Package Microsoft.Orleans.Core (注:在這裏能夠找到所需的包http://dotnet.github.io/orleans/NuGets)

記得引用完後若是在nuget裏有更新就更新一下,對於新版本,可能裏面有些庫會沒有進來,不然就會報錯,反正我是這麼作,作完這一切,項目結構以下:

在Sample.Interfaces中增長一個接口IUserService,而且繼承接口IGrainWithIntegerKey(關於這個接口有姊妹接口,關於這些接口後續會陸續講到)

代碼以下:

複製代碼
namespace Sample.Interfaces
{
  public interface IUserService:IGrainWithIntegerKey
  {
    Task<bool> Exist(string mobileNumber);
  }
}
複製代碼

在Sample.Implements項目中增長實現類UserService,而且繼承Grain(Grain這個基類同時也提供了相應的泛型實現Grain<>,關於他們的不一樣點,以及功能,後續會講到),且實現IUserService接口

代碼以下:

複製代碼
namespace Sample.Implements
{
  public class UserService : Grain, IUserService
  {
    public Task<bool> Exist(string mobileNumber)
    {
      return Task.FromResult<bool>(mobileNumber=="18612478956");
    }
  }
}
複製代碼

好了到此爲止,咱們已經開發好actor雖然簡單,接下來咱們接着增長服務啓動寄宿項(關於寄宿項,能夠是控制檯、windows服務、winfrom、asp.net ),這裏咱們採用控制檯,下面咱們建立一個服務控制檯應用程序(Server)

引用上面建立兩個項目:Sample.Implements、Sample.Interfaces。(注:其實這兩個項目不必定要引用進來,只要在生成項目的目錄下存在他們的編譯好的dll便可,silo用來自動加載啓動這個他們)

引用orleans項目中服務端的類庫(使用nuget命令:Install-Package Microsoft.Orleans.Server

項目結構以下:

代碼以下:

複製代碼
namespace Server
{
  class Program
  {
    static void Main(string[] args)
    {
      using (var host = new SiloHost("Default"))
      {
        host.InitializeOrleansSilo();
        host.StartOrleansSilo();
        Console.WriteLine("啓動成功!");
        Console.ReadLine();
        host.StopOrleansSilo();
      }
    }
  }
}
複製代碼

 

好一切準備就緒,咱們F5吧,

當你看到這得時候是否是以爲成功了,真的成功了嗎,不必定吧!

哦對了怎麼沒看到日誌呢,好咱們在項目目錄下看看日誌:

果真有日誌文件,一看文件名稱,直接告訴咱們發生錯誤了,好讓咱們打開看看吧。

從發生的異常看出,好像少了一個配置文件,在orleans 服務啓動時,須要一個配置文件,這個配置文件能夠是OrleansConfiguration.xml或者orleans.config或者orleans.config.xml

好知道緣由了,知道該怎麼作了吧,在server根目錄下建立一個xml文件OrleansConfiguration.xml,將該文件的屬性「複製到輸出目錄」值更改成"若是較新則複製"

該文件中填充配置內容,以下(詳細配置請看配置一節,此處不解釋)

複製代碼
<?xml version="1.0" encoding="utf-8" ?>
<OrleansConfiguration xmlns="urn:orleans">
<Globals>
<StorageProviders>
<Provider Type="Orleans.Storage.MemoryStorage" Name="MemoryStore" />
<Provider Type="Orleans.Storage.MemoryStorage" Name="Default" />
<!--<Provider Type="Orleans.Storage.AzureTableStorage" Name="AzureStore"/>-->
</StorageProviders>
<SeedNode Address="localhost" Port="22222"/>
<Messaging ResponseTimeout="30s"/>
</Globals>
<Defaults>
<Networking Address="localhost" Port="22222"/>
<ProxyingGateway Address="localhost" Port="40000" />
<Tracing DefaultTraceLevel="Info" TraceToConsole="false" TraceToFile="{0}-{1}.log" PropagateActivityId="false" BulkMessageLimit="1000">
<TraceLevelOverride LogPrefix="Application" TraceLevel="Info" />
<!--
<TraceLevelOverride LogPrefix="Runtime.Dispatcher" TraceLevel="Verbose" />
<TraceLevelOverride LogPrefix="AssemblyLoader.Silo" TraceLevel="Warning" />
-->
</Tracing>
<Statistics MetricsTableWriteInterval="30s" PerfCounterWriteInterval="30s" LogWriteInterval="300s" WriteLogStatisticsToTable="true" StatisticsCollectionLevel="Info"/>
</Defaults>
</OrleansConfiguration>
複製代碼
View Code

 

再次啓動F5,當看到啓動成功的輸出時,咱們再次看看生成的日誌:

此次發現比以前生成的東西多了,可是當咱們繼續往下瀏覽的時候發現有個異常:

Exc level 0: System.IO.FileNotFoundException: 未能加載文件或程序集「Microsoft.Extensions.DependencyInjection.Abstractions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60」或它的某一個依賴項。系統找不到指定的文件。
在 Orleans.Runtime.Startup.ConfigureStartupBuilder.ConfigureStartup(String startupTypeName)
在 Orleans.Runtime.Startup.ConfigureStartupBuilder.Orleans.Runtime.Startup.IStartupBuilder.ConfigureStartup(String startupTypeName)
在 Orleans.Runtime.Silo..ctor(String name, SiloType siloType, ClusterConfiguration config, ILocalDataStore keyStore)

原來是少了程序集:Microsoft.Extensions.DependencyInjection

知道緣由了,咱們經過nuget來引用這個類庫,引用成功,再次運行,而後查看日誌,異常消失,可是有個問題,每次打開日誌文件要看,是否有錯誤,或者一些關於服務的監控內容,這樣是否是很麻煩,其實咱們能夠更改一下配置信息,讓它輸出到控制檯,這樣在開發過程當中就方便多了,能夠時時看到動態信息,以下:

打開OrleansConfiguration.xml 文件找到<Tracing DefaultTraceLevel="Info" TraceToConsole="false" TraceToFile="{0}-{1}.log" 這個節點,將TraceToConsole的值更改成true保存,再次運行,以下:

好了一切都完美了,接下來咱們在繼續開發客戶端。

 

在解決方案下建立一個控制檯應用程序Client,引用客戶端相關類庫:Install-Package Microsoft.Orleans.Client

引用項目:Sample.Interfaces

代碼以下:

複製代碼
namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            System.Threading.Thread.Sleep(15000);
            GrainClient.Initialize();

            while (true)
            {
                Console.WriteLine("請輸入用戶手機號:");
                var mobileNumber = Console.ReadLine();
                //這裏因爲咱們採用的grain繼承的是IGrainWithIntegerKey ,因此咱們採用調用數值類型的key=10來建立這個grain,
                //可能有人會問key是幹嗎的,他是惟一標識這個grain的,當你指定一個key的時候,Orleans 會建立一個,它首先到
                //你的存儲介質中找(若是你配置了的話,默認採用內存存儲,這種方式適合開發期,生產環境須要保持狀態的,因此須要配置到能持久化存儲的地方去,好比sqlserver等)
                //若是找到了就直接返回,若是沒找到就根據你指定的這個key而後建立一個,這個就是grain的激活,具體詳細的,能夠看官方問的關於Grain一章。
                var userService = GrainClient.GrainFactory.GetGrain<IUserService>(10);
                //C#的一種新的表達式語法,這樣就方便多了,省的咱們拼接字符串。
                Console.WriteLine($"用戶{mobileNumber},{(userService.Exist(mobileNumber).Result?"已經存在":"不存在")}");
            }

        }
    }
}
複製代碼
View Code

 

在client項目下記得要建立配置文件,文件名稱叫作ClientConfiguration.xml

內容以下:

複製代碼
<?xml version="1.0" encoding="utf-8" ?>
<ClientConfiguration xmlns="urn:orleans">
  <Gateway Address="localhost" Port="40000"/>
  <!-- To turn tracing off, set DefaultTraceLevel="Off" and have no overrides.
    For the trace log file name, {0} is replaced by "Client" and {1} is the current time. -->
  <Tracing DefaultTraceLevel="Info" TraceToConsole="false" TraceToFile="{0}-{1}.log" BulkMessageLimit="1000">
    <TraceLevelOverride LogPrefix="Runtime" TraceLevel="Info" />
    <TraceLevelOverride LogPrefix="Application" TraceLevel="Info" />
    <TraceLevelOverride LogPrefix="AssemblyLoader" TraceLevel="Warning" />
  </Tracing>
  <Statistics MetricsTableWriteInterval="300s" PerfCounterWriteInterval="30s" LogWriteInterval="300s" WriteLogStatisticsToTable="true" StatisticsCollectionLevel="Info"/>
  <Messaging ResponseTimeout="30s" ClientSenderBuckets="8192" MaxResendCount="0"/>
</ClientConfiguration>
複製代碼
View Code

注:要記得更改文件屬性哦

 

一切準備就緒,下來讓改一下啓動方式爲多項目啓動,而後就F5等待飛吧!

終於看到勝利的果實了,哈哈!接下來咱們接着說說另一種開發方式以及發佈方式。

 

上面的這種開發方式爲了說明開發的那些具體步驟,須要引用那些類庫,以及客戶端如何去調用,步驟比較麻煩,尤爲是服務端的開發、引用類庫,也沒有相應的單元測試,接下來咱們看看另一種服務端的開發方式。

跟上面的大致步驟同樣

1.建立接口類庫

2.建立實現類庫

3.開發測試服務寄宿程序

在開始以前首先要確認一下你是否安裝了Orleans的vs模版插件,若是安裝了那麼以下圖:

若是沒有安裝,趕忙去下載一個吧地址在:http://dotnet.github.io/orleans/NuGets

找到這一節,以下圖:

點開裏面有你想要的插件,而後安裝重啓vs

1.建立接口類庫

2.建立實現類庫

 

 

3.建立服務寄宿程序

 

服務建立完以後,發現下面有自動生成的一個類OrleansHostWrapper,而且在Program下自動生成了不少代碼,代碼的大致意思就是,將服務端啓動程序的邏輯封裝在OrleansHostWrapper,而後啓動是單首創建一個應用程序域,增長一些測試例子代碼,方便多了吧,咱們不須要寫任何服務端的服務啓動代碼,在實際開發過程當中咱們只須要關心業務模塊

即接口建立,接口實現,方便多了吧。

將相應的代碼貼入進去,還記得上面出現的那個異常嗎,記得要將Microsoft.Extensions.DependencyInjection類庫引用進來哦,F5吧。

一切如預期所料,成功!

接下來咱們建立一個單元測試程序庫,方便服務端的程序單元測試Sample.Test

引用項目Sample.Implements、Sample.Interfaces

咱們建立一個測試類就叫UserServiceTest,繼承自Orleans 測試庫TestingSiloHost引用Orleans測試包 PM> Install-Package Microsoft.Orleans.TestingHost



複製代碼
 [ClassCleanup]
        public static void ClassCleanup()
        {
            // Optional. 
            // By default, the next test class which uses TestignSiloHost will
            // cause a fresh Orleans silo environment to be created.
            StopAllSilosIfRunning();
        }
複製代碼
View Code

測試代碼以下:

複製代碼
namespace Sample.Test
{
    [TestClass]
    public class UserServiceTest: TestingSiloHost
    {
        [ClassCleanup]
        public static void ClassCleanup()
        {
            // Optional. 
            // By default, the next test class which uses TestignSiloHost will
            // cause a fresh Orleans silo environment to be created.
            StopAllSilosIfRunning();
        }

        [TestMethod]
        public async void TestExist()
        {
            var grain = GrainFactory.GetGrain<IUserService>(10);
            bool bo = await grain.Exist("18612478956");
            Assert.IsTrue(bo);
        }
    }
}
複製代碼
View Code

記得增長兩個配置文件,

ClientConfigurationForTesting.xml

複製代碼
<?xml version="1.0" encoding="utf-8" ?>
<ClientConfiguration xmlns="urn:orleans">
  <Gateway Address="localhost" Port="40000"/>
  <!-- To turn tracing off, set DefaultTraceLevel="Off" and have no overrides.
    For the trace log file name, {0} is replaced by "Client" and {1} is the current time. -->
  <Tracing DefaultTraceLevel="Info" TraceToConsole="false" TraceToFile="{0}-{1}.log" BulkMessageLimit="1000">
    <TraceLevelOverride LogPrefix="Runtime" TraceLevel="Info" />
    <TraceLevelOverride LogPrefix="Application" TraceLevel="Info" />
    <TraceLevelOverride LogPrefix="AssemblyLoader" TraceLevel="Warning" />
  </Tracing>
  <Statistics MetricsTableWriteInterval="300s" PerfCounterWriteInterval="30s" LogWriteInterval="300s" WriteLogStatisticsToTable="true" StatisticsCollectionLevel="Info"/>
  <Messaging ResponseTimeout="30s" ClientSenderBuckets="8192" MaxResendCount="0"/>
</ClientConfiguration>
複製代碼
View Code

OrleansConfigurationForTesting.xml

複製代碼
<?xml version="1.0" encoding="utf-8"?>
<OrleansConfiguration xmlns="urn:orleans">
  <Globals>
    <StorageProviders>
      <Provider Type="Orleans.Storage.MemoryStorage" Name="MemoryStore" />
      <Provider Type="Orleans.Storage.MemoryStorage" Name="Default" />
      <!--<Provider Type="Orleans.Storage.AzureTableStorage" Name="AzureStore"/>-->
    </StorageProviders>
    <SeedNode Address="localhost" Port="22222"/>
    <Messaging ResponseTimeout="30s"/>
  </Globals>
  <Defaults>
    <Networking Address="localhost" Port="22222"/>
    <ProxyingGateway Address="localhost" Port="40000" />
    <Tracing DefaultTraceLevel="Info" TraceToConsole="false" TraceToFile="{0}-{1}.log" PropagateActivityId="false" BulkMessageLimit="1000">
      <TraceLevelOverride LogPrefix="Application" TraceLevel="Info" />
      <!--
       <TraceLevelOverride LogPrefix="Runtime.Dispatcher" TraceLevel="Verbose" />
       <TraceLevelOverride LogPrefix="AssemblyLoader.Silo" TraceLevel="Warning" />
       -->
    </Tracing>
    <Statistics MetricsTableWriteInterval="30s" PerfCounterWriteInterval="30s" LogWriteInterval="300s" WriteLogStatisticsToTable="true" StatisticsCollectionLevel="Info"/>
  </Defaults>
</OrleansConfiguration>
複製代碼
View Code

更改xml文件屬性爲:若是較新則複製

測試項目結構大致以下:

 

 

 

好,測試編寫完成,如何操做你懂得,這裏就不廢話了。

 

 

 接下來講一下部署,部署吧,各有各的妙招,控制檯、winform、windows服務等等,這裏我說一個框架自帶的一個部署控制檯怎麼用

記得Orleans 裏面有這麼一個程序OrleansHost.exe,他是幹什麼用的呢,對了就是用來部署的。

咱們來看看他的源碼,弄清楚他究竟是作了一件什麼事情

以下圖:

打開這個文件,能夠發現這個文件跟上面咱們經過模版建立的server中的文件OrleansHostWrapper很類似,對了,這個就是爲了保持開發部署的一致性,因此這個就能夠用來直接部署了

咱們在server的Debug下找到相應的程序,將程序複製到某個盤符好比D:\demo下面

以下圖:

而後將配置文件拷貝進來OrleansConfiguration.xml

一切準備就緒,咱們運行把,雙擊StartOrleans.cmd服務啓動

運行成功.
你會發現這個服務部署於咱們的開發互不影響,當咱們開發好一個grain的時候,直接編譯丟到這個部署目錄下,別的地方就能夠訪問了,可讓咱們重點關注業務邏輯,而不須要關心那些複雜的配置或者服務的開啓關閉等等。

實例代碼

 Orleans 之 監控工具的使用

 

這一節,咱們來講說orleans 中的幾個實用工具,OrleansHost、OrleansCounterControl、OrleansManager、ClientGenerator。

1.OrleansHost

這個工具是一個主機寄宿或者部署的一個控制檯應用程序,下面咱們看一下他的用法。

從那裏獲取呢,直接點的辦法就是在源碼包裏找到這個項目而後編譯後獲得的就是你須要的。

另一種就是當你建立服務端而後安裝了Microsoft.Orleans.Server包的時候,編譯這個項目也會在編譯包下生成出來。

我是直接編譯源碼包獲得,以下:

一些dll,值得注意的是那個Configuration包,裏面有個文件OrleansConfiguration.xsd,這個就是服務端文件配置所需的xsd驗證文件,若是咱們不知道配置文件須要那些節點的時候

能夠查看該文件,能夠將其複製到你的vs安裝目錄的xml環境中,就能夠得到智能提示了,以下:

C:\Program Files (x86)\Microsoft Visual Studio 14.0\Xml\Schemas

個人安裝目錄,若是你的安裝目錄有變,你能夠靈活調整了

看圖

而後咱們在OrleansHost.exe所在目錄下建立OrleansConfiguration.xml文件用vs打開

添加根節點OrleansConfiguration,而後增長xmlns="urn:orleans",咱們就能夠獲得智能提示了,以下圖

接下來,咱們配置一個基本的配置文件,內容以下

複製代碼
<?xml version="1.0" encoding="utf-8" ?>
<OrleansConfiguration xmlns="urn:orleans">
  <Globals>
    <StorageProviders>
      <Provider Type="Orleans.Storage.MemoryStorage" Name="MemoryStore" />
      <Provider Type="Orleans.Storage.MemoryStorage" Name="Default" />
      <!--<Provider Type="Orleans.Storage.AzureTableStorage" Name="AzureStore"/>-->
    </StorageProviders>
    <SeedNode Address="localhost" Port="22222"/>
    <Messaging ResponseTimeout="30s"/>
  </Globals>
  <Defaults>
    <Networking Address="localhost" Port="22222"/>
    <ProxyingGateway Address="localhost" Port="40000" />
    <Tracing DefaultTraceLevel="Info" TraceToConsole="true" TraceToFile="{0}-{1}.log" PropagateActivityId="false" BulkMessageLimit="1000">
      <TraceLevelOverride LogPrefix="Application" TraceLevel="Info" />
      <!--
      <TraceLevelOverride LogPrefix="Runtime.Dispatcher" TraceLevel="Verbose" />
      <TraceLevelOverride LogPrefix="AssemblyLoader.Silo" TraceLevel="Warning" />
      -->
    </Tracing>
    <Statistics MetricsTableWriteInterval="30s" PerfCounterWriteInterval="30s" LogWriteInterval="300s" WriteLogStatisticsToTable="true" StatisticsCollectionLevel="Info"/>
  </Defaults>
</OrleansConfiguration>
複製代碼
View Code

好咱們來運行一下這個看看,雙擊StartOrleans.cmd,程序跑完,有日誌文件生成,大概瀏覽一遍,不太順暢,有一些錯誤,少了一些dll的引用,少了OrleansCodeGenerator.dll,OrleansDependencyInjection.dll,再到源碼包中直接生成該項目,而後將其生成產物拷貝到StartOrleans目錄下,再次運行,一切如預料的那樣,成功:

 

這裏我整理的一個OrleansHost的部署包:OrleansHost

若有須要的能夠直接下載使用。

再來看看他的一些使用的參數:

該工具備以下參數:/? 、/help、-?、-help、/debug、deploymentid=[value]、deploymentgroup=[value]。

此次咱們先衝控制檯進入該程序所在目錄:

cmd-> d: -> cd D:\demo\OrleansHost  ,以下:

若是咱們什麼參數都不帶,改程序就直接啓動

若是下面咱們帶上如上參數看看效果:OrleansHost /? 這個是尋求幫助的一個命令

 

 OrleansHost /debug   這個是以調試環境啓動

OrleansHost  deploymentid=[value] 這個是給部署的當前silo起一個部署時的名稱而後啓動(這裏的value未來是設置爲siloname的)

OrleansHost  deploymentgroup=[value]這個值暫時尚未啓用,說不定後續版本會有用處

有個了這個工具咱們直接將開發好的grain編譯好的程序發佈到該程序目錄下就能夠了,而後重啓,不信你能夠試一試,哈哈!

 

2.OrleansCounterControl

在於OrleansHost相同的目錄下還有一個exe,就是OrleansCounterControl

以下圖:

咱們先用cmd進入該程序所在目錄下

cd C:\Demo

接下來,咱們先看看OrleansCounterControl有哪些參數

OrleansCounterControl /r,/register、/u、/unregister、/f、/force、/pause、/?、/help,咱們來看看這些參數命令。

以管理員運行,若是OrleansCounterControl不帶任何參數的時候,將運行服務端的一些序列化初始化,並輸出一些控制檯日誌,且註冊windows計數器

/r  或/register windows計數器註冊

/u 或/unregister 取消windows計數器

/f 或 /force 刪除計數器

/pause  是否有退出時提示

/? 或/help 幫助提示.

這些命令能夠組合使用,以下圖:OrleansCounterControl  /r  /pause

 

 

OrleansCounterControl  /r  /?

 

總結:這個工具用來進行管理程序的windows計數器管理工具.

3.OrleansManager

命令行定位到該程序目錄下(此工具主要是用來在客戶端,或者別的地方查看服務端上silo或者grain的一些狀態信息。)

接下來看看命令參數:

OrleansManager [ ] [/?] [-?] 執行這個命令時會有一些幫助提示

OrleansManager grainstats [silo1,silo2,...] 用來查看統計查看silo的address、激活的數量、grain類型等。

             fullgrainstats [silo1,silo2,...] 查看所有的grain的狀態

         collect  [silo1,silo2,...]  

        unregister [silo1,silo2,...] 取消註冊

         lookup  [silo1,silo2,...]

        grainreport [silo1,silo2,...] 

 

具體就再也不這裏講解了,你們在開發過程當中能夠去探索。

4.ClientGenerator

先來看看工具傳入的參數

參數要用【""】 來包裝字符串,字符串內的參數能夠用多個用【;】來分割,每一段表明一個路徑,絕對路徑,或者網絡服務地址。

具體參數使用以下:

/r:[path];[path];[path];[path]
/reference:[path];[path];[path];[path]    這個可使用的是一個目錄或者是一個文本文件內指定多個文件。

/in:[path] 若是是單個文件使用,如a.cs,或者b.cs 。

/bootstrap  
/boot

/sources:
/src:

 

就說到這裏吧。

相關文章
相關標籤/搜索