1. 程式人生 > >Orleans框架------基於Actor模型生成分布式Id

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

www 需要 http 數學 dbcontext 詳細 await clas don

一、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

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