Orleans解决并发之痛(二):Grain状态
Grains是Orleans應用程序的構建塊,它們是彼此孤立的原子單位,分布的,持久的, 一個典型的Grain是有狀態和行為的一個單實例,每個Grain實例的在單線程內執行,Grain之間共享數據通過消息傳遞,Grains是由Silo自動化管理。
Grain之間傳遞消息過程中也可能出現死鎖的情況,如:Grain A發送消息給Grain B,并等待它的完成,此時Grain B發送一個消息給Grain A,也等待其完成,這時候出現相互等待而造成死鎖。Orleans對Grain之間產生的死鎖問題解決也是非常簡單的,只需要在Grain上加[Reentrant]屬性,具體可查看官方Concurrency。
Grain狀態有好幾種存儲方式,比如:AzureTableStorage、AzureBlobStorage、SQLStorage、MemoryStorage ?等,我們還可以自定義存儲。MemoryStorage在測試項目使用沒問題,但實際生產環境要使用其他持久存儲的方式,因為一旦一個Silo被關閉,內存存儲的狀態將會消失。
在分布式下,State的使用可以減少很多對數據庫層面的壓力。當然也不是所有的Grain都推薦使用State,還是看實際業務需求。我們可以想象一個場景,一個商品的庫存如果保存在State中,所有請求都共享這個State,在判斷是否有剩余商品的時候是不是就不需要每次都去查詢數據庫了?
定義接口
public interface IPersonGrain : IGrainWithStringKey {Task SayHelloAsync(); }實現接口
public class PersonGrain : Grain, IPersonGrain {public Task SayHelloAsync(){string primaryKey = this.GetPrimaryKeyString();Console.WriteLine($"{primaryKey} said hello!");return Task.CompletedTask;} }為了實現狀態存儲,我們需要創建一個class:
public class PersonGrainState {public bool SaidHello { get; set; } }修改代碼,實現的PersonGrain不應該再繼承Grain,而是Grain<T>
[StorageProvider(ProviderName = "OrleansStorage")] public class PersonGrain : Grain<PersonGrainState>, IPersonGrain {public async Task SayHelloAsync(){string primaryKey = this.GetPrimaryKeyString();bool saidHelloBefore = this.State.SaidHello;string saidHelloBeforeStr = saidHelloBefore ? " already" : null;Console.WriteLine($"{primaryKey}{saidHelloBeforeStr} said hello!");this.State.SaidHello = true;await this.WriteStateAsync();} }第一次調用這個方法的時候this.State.SaidHello為false,輸出'xxx said hello!'。然后我們通過WriteStateAsync修改SaidHello為true,當第二次被調用的時候,從State里取出的SaidHello已經變成了true,則輸出'xxx already said hello!'
Orleans 提供了非常簡單的API來處理持久化裝狀態,看方法名就知道什么啥意思了,WriteStateAsync()、ReadStateAsync() 、 ClearStateAsync()。
同時在PersonGrain加了一個StorageProvider屬性,參數ProviderName賦值為OrleansStorage,這里需要對Silo的配置文件(OrleansConfiguration.xml)做調整,添加StorageProviders配置,Type表示存儲方式,Name表示名稱,程序內指定的ProviderName需要和配置中這個名稱保持一致。
注意:
當Name為Default時,如果某個Grain使用Default來存儲,可以不需要加StorageProvider屬性。StorageProviders下可以有多個Provider,每個Provider的Type可以不一樣,每個Grain指定的存儲方式也可以不一樣,ProviderName指定是誰就用誰存儲。
為了驗證Grain之間是獨立的,在Client加入以下代碼:
var joe = GrainClient.GrainFactory.GetGrain<IPersonGrain>("Joe"); joe.SayHelloAsync(); joe.SayHelloAsync();var sam = GrainClient.GrainFactory.GetGrain<IPersonGrain>("Sam"); sam.SayHelloAsync(); sam.SayHelloAsync();測試結果:
Test Result
SQL Server 持久存儲State
上面提到State以內存存儲的方式并不適合生產環境,那下面我們使用SQL Server來實現。
在Silo程序集中安裝依賴包:
Install-Package Microsoft.Orleans.OrleansSqlUtils Install-Package System.Data.SqlClient創建數據庫和表:
在SQL Server中創建一個數據庫,命名如:OrleansStorage(隨意);
在解決方案下找到目錄:packages\Microsoft.Orleans.OrleansSqlUtils.1.5.0\lib\net461\SQLServer,目錄下有一個.sql文件,在OrleansStorage數據庫下執行這個sql腳本即可;
修改OrleansConfiguration.xml的StorageProviders節點為:
<StorageProviders><Provider Type="Orleans.Storage.AdoNetStorageProvider"Name="OrleansStorage"AdoInvariant="System.Data.SqlClient"DataConnectionString="Server=.;Database=OrleansStorage;User ID=sa;Password=123456;"/> </StorageProviders>
重新啟動Silo和Client:
執行完成后查看數據庫中表Storage的內容,數據的值是二進制是方式存儲。
storage
之后不管重啟多少次,輸出的結果都是 "xxx already saild hello!" 。
參考鏈接:
Actor模型
Orleans
案例Demo-OrleansState
相關文章:?
.NET的Actor模型:Orleans
微軟分布式云計算框架Orleans(1):Hello World
微軟分布式云計算框架Orleans(2):容災與集群(1)
Aaron Stannard談Akka.NET 1.1
使用Akka.net開發第一個分布式應用
Orleans入門例子
Orleans例子再進一步
Orleans稍微復雜的例子—互動
Orleans簡單配置
Orleans配置---持久化
Orleans—一些概念
Orleans的集群構建
Oleans集群之Consul再解釋
Orleans解決并發之痛(一):單線程
原文地址:http://www.jianshu.com/p/ccd9cffa77bf
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的Orleans解决并发之痛(二):Grain状态的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2017(深圳) .NET技术分享交流会
- 下一篇: 【深圳】掌通宝科技有限公司技术总监(兼架