Orleans 解決并發(fā)之痛(二):Grain 狀態(tài)

Grains 是 Orleans 應(yīng)用程序的構(gòu)建塊廉沮,它們是彼此孤立的原子單位颓遏,分布的,持久的废封, 一個(gè)典型的 Grain 是有狀態(tài)和行為的一個(gè)單實(shí)例州泊,每個(gè) Grain 實(shí)例的在單線程內(nèi)執(zhí)行,Grain 之間共享數(shù)據(jù)通過(guò)消息傳遞漂洋,Grains 是由 Silo 自動(dòng)化管理遥皂。

Grain 之間傳遞消息過(guò)程中也可能出現(xiàn)死鎖的情況,如:Grain A 發(fā)送消息給 Grain B刽漂,并等待它的完成演训,此時(shí) Grain B 發(fā)送一個(gè)消息給 Grain A,也等待其完成贝咙,這時(shí)候出現(xiàn)相互等待而造成死鎖样悟。Orleans 對(duì) Grain 之間產(chǎn)生的死鎖問(wèn)題解決也是非常簡(jiǎn)單的,只需要在 Grain上加 [Reentrant] 屬性庭猩,具體可查看官方 Concurrency窟她。

Grain 狀態(tài)有好幾種存儲(chǔ)方式,比如:AzureTableStorage蔼水、AzureBlobStorage震糖、SQLStorage、MemoryStorage 等趴腋,我們還可以自定義存儲(chǔ)吊说。MemoryStorage 在測(cè)試項(xiàng)目使用沒(méi)問(wèn)題论咏,但實(shí)際生產(chǎn)環(huán)境要使用其他持久存儲(chǔ)的方式,因?yàn)橐坏┮粋€(gè) Silo 被關(guān)閉颁井,內(nèi)存存儲(chǔ)的狀態(tài)將會(huì)消失厅贪。

在分布式下,State 的使用可以減少很多對(duì)數(shù)據(jù)庫(kù)層面的壓力雅宾。當(dāng)然也不是所有的 Grain 都推薦使用 State养涮,還是看實(shí)際業(yè)務(wù)需求。我們可以想象一個(gè)場(chǎng)景秀又,一個(gè)商品的庫(kù)存如果保存在 State 中单寂,所有請(qǐng)求都共享這個(gè) State,在判斷是否有剩余商品的時(shí)候是不是就不需要每次都去查詢數(shù)據(jù)庫(kù)了吐辙?

定義接口

public interface IPersonGrain : IGrainWithStringKey
{
  Task SayHelloAsync();
}

實(shí)現(xiàn)接口

public class PersonGrain : Grain, IPersonGrain
{
  public Task SayHelloAsync()
  {
    string primaryKey = this.GetPrimaryKeyString();

    Console.WriteLine($"{primaryKey} said hello!");

    return Task.CompletedTask;
  }
}

為了實(shí)現(xiàn)狀態(tài)存儲(chǔ),我們需要?jiǎng)?chuàng)建一個(gè) class:

public class PersonGrainState
{
  public bool SaidHello { get; set; }
}

修改代碼蘸劈,實(shí)現(xiàn)的 PersonGrain 不應(yīng)該再繼承 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();
  }
}

第一次調(diào)用這個(gè)方法的時(shí)候 this.State.SaidHello 為 false,輸出 xxx said hello! 威沫。然后我們通過(guò) WriteStateAsync 修改 SaidHello 為 true贤惯,當(dāng)?shù)诙伪徽{(diào)用的時(shí)候,從 State 里取出的 SaidHello 已經(jīng)變成了 true棒掠,則輸出 xxx already said hello!

Orleans 提供了非常簡(jiǎn)單的 API 來(lái)處理持久化裝狀態(tài)孵构,看方法名就知道什么意思了,WriteStateAsync()烟很、ReadStateAsync() 颈墅、 ClearStateAsync()。

同時(shí)在 PersonGrain 加了一個(gè) StorageProvider 屬性雾袱,參數(shù) ProviderName 賦值為 OrleansStorage恤筛,這里需要對(duì) Silo 的配置文件(OrleansConfiguration.xml)做調(diào)整,添加 StorageProviders 配置芹橡,Type 表示存儲(chǔ)方式毒坛,Name 表示名稱,程序內(nèi)指定的 ProviderName 需要和配置中這個(gè)名稱保持一致林说。

注意:
當(dāng) Name為Default 時(shí)煎殷,如果某個(gè) Grain 使用 Default 來(lái)存儲(chǔ),可以不需要加 StorageProvider 屬性腿箩。StorageProviders 下可以有多個(gè) Provider豪直,每個(gè) Provider 的 Type 可以不一樣,每個(gè) Grain 指定的存儲(chǔ)方式也可以不一樣度秘,ProviderName 指定是誰(shuí)就用誰(shuí)存儲(chǔ)顶伞。

<?xml version="1.0" encoding="utf-8" ?>
<OrleansConfiguration xmlns="urn:orleans">
  <Globals>
    <SeedNode Address="localhost" Port="11111" />
    <StorageProviders>
      <Provider Type="Orleans.Storage.MemoryStorage"
                Name="OrleansStorage" />
    </StorageProviders>
  </Globals>
  <Defaults>
    <Networking Address="localhost" Port="11111" />
    <ProxyingGateway Address="localhost" Port="30000" />
  </Defaults>
</OrleansConfiguration>

為了驗(yàn)證 Grain 之間是獨(dú)立的饵撑,在 Client 加入以下代碼:

 var joe = GrainClient.GrainFactory.GetGrain<IPersonGrain>("Joe");
joe.SayHelloAsync();
joe.SayHelloAsync();

var sam = GrainClient.GrainFactory.GetGrain<IPersonGrain>("Sam");
sam.SayHelloAsync();
sam.SayHelloAsync();

測(cè)試結(jié)果:

Test Result

SQL Server 持久存儲(chǔ) State

上面提到 State 以內(nèi)存存儲(chǔ)的方式并不適合生產(chǎn)環(huán)境,那下面我們使用 SQL Server 來(lái)實(shí)現(xiàn)唆貌。

在 Silo 程序集中安裝依賴包:
Install-Package Microsoft.Orleans.OrleansSqlUtils
Install-Package System.Data.SqlClient
創(chuàng)建數(shù)據(jù)庫(kù)和表:
  1. 在 SQL Server 中創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)滑潘,命名如:OrleansStorage(隨意);
  2. 在解決方案下找到目錄:packages\Microsoft.Orleans.OrleansSqlUtils.1.5.0\lib\net461\SQLServer锨咙,目錄下有一個(gè) .sql 文件语卤,在 OrleansStorage 數(shù)據(jù)庫(kù)下執(zhí)行這個(gè) sql 腳本即可;
修改 OrleansConfiguration.xml 的 StorageProviders 節(jié)點(diǎn)為:
<StorageProviders>
  <Provider Type="Orleans.Storage.AdoNetStorageProvider"
            Name="OrleansStorage"
            AdoInvariant="System.Data.SqlClient"
            DataConnectionString="Server=.;Database=OrleansStorage;User ID=sa;Password=123456;"/>
</StorageProviders>
重新啟動(dòng) Silo 和 Client:

執(zhí)行完成后查看數(shù)據(jù)庫(kù)中表 Storage 的內(nèi)容酪刀,數(shù)據(jù)的值是二進(jìn)制是方式存儲(chǔ)粹舵。


storage

之后不管重啟多少次,輸出的結(jié)果都是 xxx already saild hello! 骂倘。

參考鏈接:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末眼滤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子历涝,更是在濱河造成了極大的恐慌诅需,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荧库,死亡現(xiàn)場(chǎng)離奇詭異堰塌,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)分衫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)场刑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蚪战,你說(shuō)我怎么就攤上這事牵现。” “怎么了屎勘?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵施籍,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我概漱,道長(zhǎng)丑慎,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任瓤摧,我火速辦了婚禮竿裂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘照弥。我一直安慰自己腻异,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布这揣。 她就那樣靜靜地躺著悔常,像睡著了一般影斑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上机打,一...
    開(kāi)封第一講書(shū)人閱讀 50,050評(píng)論 1 291
  • 那天矫户,我揣著相機(jī)與錄音,去河邊找鬼残邀。 笑死皆辽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的芥挣。 我是一名探鬼主播驱闷,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼空免!你這毒婦竟也來(lái)了空另?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蹋砚,失蹤者是張志新(化名)和其女友劉穎痹换,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體都弹,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年匙姜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了畅厢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡氮昧,死狀恐怖框杜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情袖肥,我是刑警寧澤咪辱,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站椎组,受9級(jí)特大地震影響油狂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寸癌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一专筷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蒸苇,春花似錦磷蛹、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)庇勃。三九已至,卻和暖如春槽驶,著一層夾襖步出監(jiān)牢的瞬間责嚷,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工捺檬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留再层,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓堡纬,卻偏偏與公主長(zhǎng)得像聂受,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子烤镐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理蛋济,服務(wù)發(fā)現(xiàn),斷路器炮叶,智...
    卡卡羅2017閱讀 134,638評(píng)論 18 139
  • Spark SQL, DataFrames and Datasets Guide Overview SQL Dat...
    草里有只羊閱讀 18,307評(píng)論 0 85
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,858評(píng)論 25 707
  • 租的房子有點(diǎn)遭碗旅,不知道該如何整理,慢慢來(lái)吧镜悉!加油吧祟辟!在未來(lái)的九個(gè)月會(huì)有美好的時(shí)光。沒(méi)有醫(yī)院的全貌照片侣肄,改天附上
    木在林夕閱讀 243評(píng)論 0 0
  • 我可以失敗旧困,但不能慫。 前一段時(shí)間看《奇葩說(shuō)》稼锅,里面有一個(gè)辯題:“我們終究會(huì)變成自己討厭的人”是不是一件壞事吼具?當(dāng)時(shí)...
    一個(gè)90后閱讀 199評(píng)論 0 0