Orleans解决并发之痛(一):单线程
程序在運行過程中有時會莫名其妙出現(xiàn)代碼的某些約束或者執(zhí)行結(jié)果和理想狀況不一樣,正常邏輯怎么會出現(xiàn)這樣的情況?到底發(fā)生了什么?好像見了鬼!瞬間好無助。
誰來救救我
大多數(shù)出現(xiàn)正常邏輯很難解釋的時候,我們可能會想到并發(fā)問題,因為好像只有并發(fā)才會能說服自己。為了驗證和解決這個問題,我們可能會嘗試一些方案,在并發(fā)的情況下我相信很多人都使用過鎖,鎖確實也能幫忙我們解決問題,不然它干嘛存在。
但隨著業(yè)務邏輯的持續(xù)復雜,鎖的使用可能無處不在。首先大家都知道鎖本身的機制很耗性能;然后鎖本身不涉及什么編程模式,所以在業(yè)務代碼中融入大量鎖對代碼本身的穩(wěn)定性也有一定影響。
經(jīng)過查找資料,因為本身的項目是基于.NET,所以發(fā)現(xiàn)Microsoft Orleans好像可以比較好的滿足解決并發(fā)的需求。
Orleans之前,先來扯一扯Actor模型
Actor是以單線程存在的,所有消息都是順序到達的,每次收到消息后,就放入隊列,而它每次也從隊列中取出消息體來處理;
每一個Actor有一個Id和它對應,一個Id對應的Actor只會在集群中存在一個,使用者只需要通過Id就能隨時訪問不需要關(guān)注該Actor在集群的什么位置;
每一個Actor看作是一個獨立的實體,擁有自己獨立的狀態(tài)。Actor與Actor之間可以進行消息通知;
注:有狀態(tài)的 Actor在集群中一個Id只會存在一個實例,無狀態(tài)的可配置為根據(jù)流量存在多個,無狀態(tài)的情況看具體業(yè)務需求。
Actor System
再來扯一扯Orleans框架
Orleans 提供了一個簡單的方法來構(gòu)建大規(guī)模、高并發(fā)、分布式應用程序,被認為是Actor模型的分布式版本,是一種改進的Actor模型。在Orleans中,Actors被稱作Grains,采用接口來表示,Actors的消息用異步方法來接收,方法返回值必須是Task or Task<T>。
Orleans幾個核心角色:
Grains(Actors)
Grains是Orleans應用程序的業(yè)務邏輯實現(xiàn)與抽象,Grains是彼此孤立的原子單位,分布的,持久的。 一個典型的Grain是有狀態(tài)和行為的一個單實例。
Silo
Silo是一個主機服務,里面主要用于執(zhí)行Grains,也就是說Grains開發(fā)完成后需要注冊到Silo中,然后等待調(diào)用。它監(jiān)聽一個端口,用來監(jiān)聽從Silo到Silo的消息或者從客戶端到Silo的消息的,典型的Silo就是,每臺機器運行一個Silo,會對外暴露網(wǎng)關(guān)地址供調(diào)用。
Cluster(集群搭建的時候會具體介紹)
大量的Silo同時在一起工作就形成了Orleans的集群,Orleans運行完全自動化的集群管理。
Client
具體的應用客戶端,可以是控制臺、Web應用程序、WPF等一切.NET端技術(shù)。
開始接觸Orleans Sample的時候,第一感覺項目結(jié)構(gòu)和gRPC還挺像的,如果你之前有接觸,一定感覺很親切:
定義一個接口(Interfaces)
實現(xiàn)接口(Grains) -- 添加引用Interfaces
啟動服務端(Silo)-- 添加引用Interfaces,Grains
啟動客戶端 (Client)-- 添加引用Interfaces
練習過程中對Nuget安裝Orleans相關(guān)依賴包可能會有一些模糊,這里說明一下我的具體步驟,希望盡快幫忙實現(xiàn)效果,所有程序集使用.Net Framework的版本都是4.6:
| Interfaces | 類庫 | Core | - |
| Grains | 類庫 | Core | Interfaces |
| Silo | 控制臺程序 | Core OrleansCodeGenerator OrleansProviders OrleansRuntime | Interfaces Grains |
| Client | 控制臺程序 | Core OrleansCodeGenerator | Interfaces |
在Silo項目中添加配置文件 OrleansConfiguration.xml:
<?xml version="1.0" encoding="utf-8" ?> <OrleansConfiguration xmlns="urn:orleans"><Globals><SeedNode Address="localhost" Port="11111" /></Globals><Defaults><Networking Address="localhost" Port="11111" /><ProxyingGateway Address="localhost" Port="30000" /></Defaults> </OrleansConfiguration>SeedNode:集群中主Silo地址,生產(chǎn)環(huán)境下不要這么使用。以這種方式配置主Silo的情況下,其他Silo加入集群需要等主Silo先啟動。之后會介紹SystemStore來維護集群成員關(guān)系;
Networking:內(nèi)部Silo與Silo之間通信地址;
ProxyingGateway:客戶端調(diào)用的網(wǎng)關(guān)地址;
在Client項目中添加配置文件 ClientConfiguration.xml:
<?xml version="1.0" encoding="utf-8" ?> <ClientConfiguration xmlns="urn:orleans"><Gateway Address="localhost" Port="30000"/> </ClientConfiguration>Gateway:配置Silo對外的網(wǎng)關(guān)地址;
集群下可以配置多個Gateway節(jié)點,如下:
<Gateway Address="gateway1" Port="30000"/> <Gateway Address="gateway2" Port="30000"/>注意:配置文件需要設(shè)置屬性 "復制到輸出目錄"
configuration
Grain說明:
每個Grain都是單實例的,具有唯一標識。根據(jù)唯一標識獲取Grain,這個標識可以是GUID、String、Long、混合類型。
在Grain內(nèi)如果發(fā)送消息給其他Grain,需要使用 this.GrainFactory.GetGrain,不能通過 GrainClient.GrainFactory.GetGrain。
var test = GrainClient.GrainFactory.GetGrain<ITest>(0); // long類型的primaryKey 0 public class TestGrain : Orleans.Grain, ITest {private int num = 0;public Task AddCount(){num++;Console.WriteLine(num);return Task.CompletedTask;} }Client說明:
同時啟動3個Task,每個Task內(nèi)并行200次調(diào)用AddCount方法。如果沒有做特殊的處理,num的結(jié)果肯定是亂的,并不會出現(xiàn)一直累加的效果。
private static void DoClientWork() {var t1 = Task.Factory.StartNew(() =>{AddCount();});var t2 = Task.Factory.StartNew(() =>{AddCount();});var t3 = Task.Factory.StartNew(() =>{AddCount();});Task.WaitAll(t1, t2, t3); }static void AddCount() {var test = GrainClient.GrainFactory.GetGrain<ITest>(0);Parallel.For(0, 200, (i) =>{test.AddCount();}); }實際上執(zhí)行最終的結(jié)果是600,并不會出現(xiàn)不一致的變化效果,這足以說明同一個Grain內(nèi)部是單線程執(zhí)行。
Test Result
相關(guān)文章:?
.NET的Actor模型:Orleans
微軟分布式云計算框架Orleans(1):Hello World
微軟分布式云計算框架Orleans(2):容災與集群(1)
Aaron Stannard談Akka.NET 1.1
使用Akka.net開發(fā)第一個分布式應用
Orleans入門例子
Orleans例子再進一步
Orleans稍微復雜的例子—互動
Orleans簡單配置
Orleans配置---持久化
Orleans—一些概念
Orleans的集群構(gòu)建
Oleans集群之Consul再解釋
原文地址:http://www.jianshu.com/p/141ea382d242
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的Orleans解决并发之痛(一):单线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#使用Xamarin开发可移植移动应用
- 下一篇: ASP.NET Core 运行原理解剖[