Grain 是 Orleans 中的重要素素。它是組成 Orleans 應用程序的基本組成部分,具有原子性,事務被封裝在 Grain 中,與其它Grain互相隔離,并在分布式網絡上進行分發(fā)。對開發(fā)人員來說,Grain封裝了實體的狀態(tài)和邏輯,并可以通過彼此間的公開方法進行交互。
Orleans 通過下面的特性,簡化構建可伸縮的應用,降低開發(fā)人員處理并發(fā)的門檻:
- 不要在 Grain 間共享數據,除非通過 Grain 間的消息傳遞
- 通過單線程運行每個 Grain 的邏輯來保證 Grain 的高可用性
一個經典的 Grain 將實體的狀態(tài)和行為封裝在 Grain中
Grain 標識
每個 Grain實例具有一個唯一的地址,并通過一個 grain key進行標識,grain key 可以是如下類型:
- Guid
- string
- long
- long + string
- Guid + string
調用 Grain
一個 Grain 實現一個或多個 IGrain 接口,要調用一個Grain,開發(fā)者需要知道它實現了哪個接口,以及相應的 grain key,如下代碼將通過用戶的電子郵件獲取一個用戶配置(IUserProfile)的實例,并調用它的UpdateAddress方法修改此用戶的聯(lián)系地址。
var user = grainFactory.GetGrain<IUserProfile>(userEmail);
await user.UpdateAddress(newAddress);
注意:我們不需要像平時編程一樣通過new或者DI去實例化一個Grain,我們調用它的時候,它就已經激活并準備好被我們使用了。這是Orleans的魅力所在:我們永遠不需要創(chuàng)建、實例化或刪除 Grain,好像所有可能的 Grain (例如有成千上百萬的用戶配置實例)始終在內存中等待我們調用;這都是因為有 Orleans Runtime 在幕后替我們管理這些繁重的工作。
Grain 的生命周期
Grain 生存在稱為 Silo 的容器中。眾多的 Silo 組成了 Orleans 集群。當用戶請求訪問一個Grain時,Orleans 先會檢查集群中的 Silo 上是否有一個該 Grain 的實例在運行,如果沒有則會進行創(chuàng)建,這個過程稱為激活;如果 Grain 使用一持久化特性,那么在激活時會自動從存儲中加載狀態(tài)來初始化這個 Grain。
一旦 Grain 在Silo 中激活,那么它就開始接受調用(外部調用、內部 Grain 間的調用)。在處理請求的過程中,Grain 可能會調用其它的 Grain 或一些外部服務。
如果 Grain 空閑一段時間(可配置)后, Orleans 會從內存中移除Grain 以釋放其資源。等到再次請求調用此 Grain 時再次它激活。但不一定在之前的 Silo 上(對開發(fā)者來說,不需要關注Grain 在哪一個Silo,即不必關注 Grain的物理位置,開發(fā)者永遠通過grain key調用它)

Orleans 控制著 Grain 激活、停用的過程,開發(fā)人員進行開發(fā)時不必理會,只需假設所有的 Grain 均處于激活狀態(tài),直接調用。
Grain 生命周期中的關鍵事件序列如下:
- 其它 Grain 或客戶端調用此 Grain 的方法
- 激活 Grain(如果它沒有在任何一個集群中被激活)
- 執(zhí)行 Grain 構造
- 如果使用了持久化策略,將從存儲中加載 Grain 狀態(tài)
- 如果有重寫
OnActivateAsync,將在此時被觸發(fā)
- Grain 處理傳入的請求
- Grain 空閑一段時間
- Silo runtime 決定停止一個 Grain
- 如果有重寫
OnDeactivateAsyncSilo runtime 先觸發(fā)OnDeactivateAsync - Silo runtime 從內存移除 Grain
Grain執(zhí)行
Grain在塊中執(zhí)行,并在執(zhí)行下一個塊之前必須完成當前塊的工作(其中包括響應來自其它 Grain 或 Client 的方法調用)。
一個塊中執(zhí)行的基本單位稱之為流。
Orleans 可以并行執(zhí)行多個激活的流,且每個只執(zhí)行一次。這個機制保障了不需要使用鎖或其他同步方法來防止數據競爭或多線程危險。