Orleans框架------基於Actor模型生成分布式Id


一、Actor簡介

actor模型是一種並行計算的數學模型。 響應於收到的消息,演員可以:做出決定,創建更多Actor,發送更多消息,並確定如何響應接收到的下一條消息。 演員可以修改自己的狀態,但只能通過消息相互影響(避免需要任何鎖)。

     actor是一個計算實體,當其收到消息時,可以並發執行如下操作:

   1. 發送有限數量的消息給其他actor

   2. 創建有限數量的新actor

   3. 指定收到下一消息時的行為

   在Orleans中使用的是虛擬Actor方式,詳細:http://dotnet.github.io/orleans/Documentation/Introduction.html

    詳細參見: https://en.wikipedia.org/wiki/Actor_model

 

二、Orleans框架

      Orleans是一個框架,可以直接構建分布式大規模計算應用程序,而無需學習和應用復雜的並發或其他縮放模式。 它是由Microsoft Research創建的,旨在用於雲端。

Orleans已被Microsoft Azure廣泛應用於微軟的幾個產品集團,其中最着名的是343個行業,作為所有Halo 4和Halo 5雲服務的平台,以及越來越多的其他公司。

  特性:

    1、默認可擴展
           奧爾良處理構建分布式系統的復雜性,使您的應用程序能夠擴展到數百台服務器。
    2、低延遲
           奧爾良允許您保持內存所需的狀態,因此您的應用程序可以快速響應傳入的請求。

    3、簡化並發

          Orleans允許您編寫簡單的單線程C#代碼,通過actor之間的異步消息傳遞來處理並發。

 

 

三、生成流水號項目實戰

      1、場景

          現在系統基於分布式服務開發,數據在客戶端處理后提交到服務端入庫,但是由於多個系統間的並發而流水號全部在一張表,每次都是先select在update 高並發容易直接死鎖。

     如圖:

 

 

     2、基於Orleans的actor

        將每條數據改造成一個Actor,由個Actor之間的狀態來保證流水號的遞增,這樣即使單個流水號訪問量大只要擴展Orleans的soli即可。

 

四、關鍵代碼:

         1、利用初始化SerialNumberStorgeProvider初始化管理Grain的狀態

 

 

    
public class SerialNumberStorgeProvider : IStorageProvider { public Logger Log { get; set; } public string Name { get; set; } public Task ClearStateAsync(string grainType, GrainReference grainReference, IGrainState grainState) { return TaskDone.Done; } public Task Close() { return TaskDone.Done; } public Task Init(string name, IProviderRuntime providerRuntime, IProviderConfiguration config) { this.Name = nameof(SerialNumberStorgeProvider); this.Log = providerRuntime.GetLogger(this.Name); return TaskDone.Done; } public Task ReadStateAsync(string grainType, GrainReference grainReference, IGrainState grainState) { Console.WriteLine("獲取種子信息"); var SerialNumber = grainReference.GetPrimaryKeyString(); using (var db = new DBContext()) { var query = db.SerialNumbers.AsNoTracking().FirstOrDefault(o => o.Name.Equals(SerialNumber)); if (query != null) grainState.State = query; else { db.SerialNumbers.Add(new SerialNumberInfo { Name = grainReference.GetPrimaryKeyString(), Number = 1 }); db.SaveChanges(); grainState.State = new SerialNumberInfo { Name = grainReference.GetPrimaryKeyString(), Number = 1 }; } } return TaskDone.Done; } public async Task WriteStateAsync(string grainType, GrainReference grainReference, IGrainState grainState) { var model = grainState.State as SerialNumberInfo; using (var db = new DBContext()) { var query = db.SerialNumbers.FirstOrDefault(o => o.Name.Equals(model.Name)); query.Number = model.Number; await db.SaveChangesAsync(); } } }

   

2、Grain獲取流水號的實現  
[StorageProvider(ProviderName = "SerialNumberStorgeProvider")] public class SerialNumberGrain : Grain<SerialNumberInfo>, ISerialNumberGrain { /// <summary>
        /// 獲取多個流水號 /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        public Task<List<long>> GetMutilSerialNumber(int number) { if (number == 0) { number = 1; } List<long> list = new List<long>(); for (int i = 1; i <= number; i++) { this.State.Number += 1; list.Add(this.State.Number); } this.WriteStateAsync(); return Task.FromResult(list); } /// <summary>
        /// 獲取單個流水號 /// </summary>
        /// <returns></returns>
        public Task<long> GetSerialNumber() { this.WriteStateAsync(); return Task.FromResult(this.State.Number); } }

 

 
        

最后,最多說一句,在測試的時候發現如果不適用如下這種方式,並發時會發生Task調度異常

 

 

源碼地址:https://github.com/liyang-live/MakeSerialNumber

 

    參考資料:http://dotnet.github.io/orleans/index.html

                     http://www.cnblogs.com/joab/p/5657851.html

                     https://github.com/dotnet/orleans


免責聲明!

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



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