Actor
是一種高併發處理模型,每個Actor
都有著自己的狀態有序訊息處理機制,所以在業務處理的情況並不需要制定鎖的機制,從而達到更高效的處理能性。XRPC
是一個基於遠端介面呼叫的RPC
元件,它可以簡單地實現高效能的遠端介面呼叫;XRPC
在建立遠端介面時是支援針對介面建立對應的Actor
例項。當建立介面Actor
後,所有Client針對這一例例項Actor
的所有方法呼叫都是有序處理。以下介紹如何在XRPC
建立並使用Actor
什麼場景適用於Actor
既然每個Actor
都有著自己的狀態和順序處理機制,那可以針對這優點進行相關業務的展開;在棋牌遊戲中的桌子可以是一個Actor
,車票裡的每一輛車可以是一個Actor
,秒殺裡的每一種商品是一個Actor
。在這些Actor
所有的操作都是有序進行,不存在鎖,也不需要事務(通過EventSourcing來保障)和不會產生死鎖;資料變更全記憶體操作,通過這樣可以大提高業務的處理效能。
引用XRPC
Install-Package BeetleX.XRPC
定義Actor
的RPC服務
XRPC支援的Actor
服務功能是建立在EventNext
之上,它的好處是直接介面行為的Actor
建立,而不是傳統的訊息加接收方法,這樣在應用設計和呼叫上也是非常方便靈活。接下來定義一個簡單的Actor
服務
- 介面定義
1 public interface IAmountService 2 { 3 Task<int> Income(int amount); 4 Task<int> Payout(int amount); 5 Task<int> Get(); 6 }
-
實現介面
1 [Service(typeof(IAmountService))] 2 public class AmountService : ActorState, IAmountService 3 { 4 5 private int mAmount; 6 7 public override Task ActorInit(string id) 8 { 9 return base.ActorInit(id); 10 } 11 12 public Task<int> Get() 13 { 14 return mAmount.ToTask(); 15 } 16 17 public Task<int> Income(int amount) 18 { 19 mAmount += amount; 20 return mAmount.ToTask(); 21 } 22 23 public Task<int> Payout(int amount) 24 { 25 mAmount -= amount; 26 return mAmount.ToTask(); 27 } 28 }
-
啟動對應的RPC服務
private static XRPCServer mXRPCServer; static void Main(string[] args) { mXRPCServer = new XRPCServer(); mXRPCServer.ServerOptions.LogLevel = LogType.Error; mXRPCServer.Register(typeof(Program).Assembly); mXRPCServer.Open(); Console.Read(); }
以上程式碼是在預設埠
9090
上繫結RPC
服務,可以通過執行日誌檢視服務啟動情況
建立遠端Actor
呼叫
- 建立RPC Client
1 client = new XRPCClient("192.168.2.18", 9090); 2 client.Connect();
RPC
客戶端,通過它的Create
可以建立介面代理 - 建立介面Actor例項
IAmountService henry = client.Create<IAmountService>("henry"); IAmountService ken = client.Create<IAmountService>("ken");
IAmountService
建立了兩個Actor
物件,這兩個物件的操作都是相互隔離互不干擾;每個Actor
物件中的方法在併發下都是有序執行,並不會產生執行緒安全問題,所以在不同方法中操作對像的資料成員都不需要鎖來保證資料安全性。
測試
為了更好地驗證Actor
的隔離和併發安全性,簡單地併發測試一下
1 for (int i = 0; i < concurrent; i++) 2 { 3 var task = Task.Run(async () => 4 { 5 for (int k = 0; k < requests; k++) 6 { 7 await henry.Income(10); 8 System.Threading.Interlocked.Increment(ref mCount); 9 } 10 }); 11 tasks.Add(task); 12 task = Task.Run(async () => 13 { 14 for (int k = 0; k < requests; k++) 15 { 16 await henry.Payout(10); 17 System.Threading.Interlocked.Increment(ref mCount); 18 } 19 }); 20 tasks.Add(task); 21 task = Task.Run(async () => 22 { 23 for (int k = 0; k < requests; k++) 24 { 25 await ken.Income(10); 26 System.Threading.Interlocked.Increment(ref mCount); 27 } 28 }); 29 tasks.Add(task); 30 task = Task.Run(async () => 31 { 32 for (int k = 0; k < requests; k++) 33 { 34 await ken.Payout(10); 35 System.Threading.Interlocked.Increment(ref mCount); 36 } 37 }); 38 tasks.Add(task); 39 } 40 await Task.WhenAll(tasks.ToArray()); 41 double useTime = EventCenter.Watch.ElapsedMilliseconds - start; 42 Console.WriteLine($"Completed count:{mCount}|use time:{useTime}|rps:{(mCount / useTime * 1000d):###.00} |henry:{await henry.Get()},ken:{await ken.Get()}");
兩個程式同時在本機跑了一下,在50併發的情況大概是11萬RPS
服務中的Actor
隔離性
服務是通過名稱來例項化介面的不同Actor
,同一服務即使多個Client
同時對一名稱的Actor
進行建立服務也可以保證它的唯一性。