Orleans入门例子
Orleans是微軟開源的分布式actor模型框架.actor模型的原理網(wǎng)絡(luò)上有很多文章.有許多理論性的文章,深刻地我都不知道怎么應(yīng)用.在這里我就不贅述了.既然是博客,就說說自己的理解。
對于編程來說,不管是前臺還是后臺,在現(xiàn)在的計(jì)算機(jī)環(huán)境下,多線程編程是不可避免的。多線程帶來的很多好處,也帶來的很多編程上的“壞處”。好處就是,什么并發(fā)、高效,高資源利用率等等高大上的詞語。這些就不詳細(xì)說了。就說說壞處.
多線程帶來的最大的變化就是:我先前保存的變量A,在我想要再次利用它的時(shí)候,A是否被其他線程給更改了?數(shù)據(jù)庫中叫臟讀,我們拿過來用,“避免臟讀”又有個(gè)同義詞叫做“操作的原子性”。為了解決臟讀的問題,有一下幾種辦法:?
辦法一變量A在初始賦值的時(shí)候,就規(guī)定它不可以被更改,就是不可變,英文叫做immutable,很多函數(shù)式語言如F#,scala等都有不可變量。既然量是不可變的。就不會(huì)有什么臟讀問題了。這真是個(gè)好辦法,但是在C#和JAVA這樣的語言里,這不是個(gè)好辦法。
辦法二就是大家經(jīng)常使用的,多線程,架鎖。精巧的設(shè)計(jì)類,小心的使用這些來,管理各種資源的競爭。為了避免臟讀,在C#以及JAVA里,架鎖是一個(gè)可行的辦法。但是這樣會(huì)使得效率變低(鎖架地多了,這個(gè)會(huì)更明顯),更為關(guān)鍵是帶來邏輯的復(fù)雜性。架鎖的程序,可以說是一個(gè)“正確但是脆弱”的程序。離開了初創(chuàng)人員,后來的人想讀懂讀透?呵呵,人生苦短。
辦法三,也是Orleans采用的辦法,設(shè)計(jì)一個(gè)類,保證這個(gè)類從“創(chuàng)建”過程到“銷毀”過程,以及“中途調(diào)用它的方法”的執(zhí)行過程,這三個(gè)“過程”不管何時(shí)何地調(diào)用,都會(huì)在同一個(gè)線程中運(yùn)行。這樣的化,我就可以放心大膽的去修改,調(diào)用這個(gè)類的方法,而不用擔(dān)心這個(gè)指向這個(gè)類的變量出現(xiàn)“臟讀”的情況。這個(gè)某種程度上算是辦法一和二的折中。為了說明這個(gè)辦法,就不得不說Actor模型。?
人與人的溝通只是通過消息,不管是聽聞嗅觸等等感覺,都是往外界往人的身體發(fā)送的消息.人處理后做出反應(yīng).這個(gè)處理與反應(yīng)是異步的.接受消息和處理消息都是并行的、同時(shí)的進(jìn)行。actor模型就是模擬這種通信方式.用Actor作為名稱,也算是指明了這個(gè)模型想要模擬什么。
既然是模擬,那就是簡單化。Orleans模型里,每一個(gè)actor有專門的類代表它,叫做Grain,這個(gè)grain類就是模擬通信場景中的”人”,但是Orleans為了解決多線程帶來的“資源競爭”等問題,采用了辦法三,再Orleans框架內(nèi),它保證每個(gè)grain類符合以下行為規(guī)范:
A. 發(fā)往同一個(gè)grain類實(shí)例的任何消息都會(huì)在固定線程內(nèi)執(zhí)行。
B. grain類按照接受消息的先后,依次處理消息。在任意時(shí)間點(diǎn),一個(gè)grain實(shí)例只處理一個(gè)消息。
C. grain實(shí)例內(nèi)的字段屬性,只能由實(shí)例本身訪問。外界不能訪問。?
在大部分情況下,這些規(guī)范都是成立的。這些規(guī)范被稱為“單線程機(jī)制”。但是也要有“變通”,這些“變通”涉及到很多其他方面,在以后的文章中會(huì)解釋是如何變通的,為什么要變通,以及什么場景下才需要變通。不過現(xiàn)在你可以認(rèn)為,這些規(guī)則就是“法律”。在Orleans的框架內(nèi),這些grain們都是一些“頭腦簡單并且自閉的人”。你作為設(shè)計(jì)者,如果恰到好處的布置這些“人”,它們會(huì)給你驚喜。比如對于消息的處理,可以做到全程無鎖等。。。高大上的特性。
同時(shí)Orleans是使用Actor模型的,同時(shí)微軟更近了一步,它是使用的虛擬的Actor模型。具體怎么個(gè)虛擬法,這個(gè)以后的文章中解釋。
以上說了那么多,就說明Orleans是使用actor模型的。還沒有說分布。Orleans是分布式的。那是因?yàn)樗蠥ctor之間的互動(dòng)是跨線程的,甚至是跨計(jì)算機(jī)的。同時(shí)Orleans框架保證開啟多個(gè)服務(wù)端程序?qū)嵗?#xff0c;通過簡單的配置,它們的行為就會(huì)如同單一的服務(wù)端實(shí)例一樣。客戶端感覺不到區(qū)別,簡單的說就是服務(wù)端能“合體”。兩個(gè)小奧特曼,每個(gè)小奧特曼由更小的細(xì)胞組成,同時(shí)他們又合體變成了一個(gè)大的奧特曼,就問你怕不怕,你如果不怕,Orleans甚至實(shí)現(xiàn)了“合體的合體”---這種橫向擴(kuò)展性為高可用,資源的充分利用以及大吞吐量帶來可很大的想象空間,同時(shí)也是靈活部署的基礎(chǔ)。另外利用Orleans的Actor模型,也可以很容的實(shí)現(xiàn)event sourcing。它甚至已經(jīng)內(nèi)置了event sourcing中間件。
Orleans同時(shí)也有Net.Core版本,利用它可以實(shí)現(xiàn)跨平臺運(yùn)行,它名字叫做Orleans vNext.我是按照framwork版本寫的。兩個(gè)版本的使用方法幾乎相同。
這個(gè)世界有很多actor模型框架,JAVA和Scala的AKKA,C#的AKKA.Net以及Orleans。它們都是開源的,都可以去下載它們的源碼學(xué)習(xí)。在寫文章的時(shí)候,Orleans版本剛剛是1.5版本,所以我就只能按照1.5版本來寫。
要讀的非常的順暢,我假定讀者都熟悉C#,熟悉多線程,特別是C#中Task類的用法,雖然很多時(shí)候,這些沒有這些知識也能非常順利的讀懂,但是有,會(huì)在關(guān)鍵的時(shí)刻幫助你理解Orleans。更進(jìn)一步,如果熟悉了以上知識,才能明白Orleans能不能幫助你解決實(shí)際工作中的問題。以及在技術(shù)選型的時(shí)候,明白Orleans是不是你想要的。
我初步計(jì)劃寫8篇左右.爭取把Orleans的所有主要方面介紹完畢,初步目的是看完之后能用Orleans處理問題.
我曾經(jīng)對Orleans進(jìn)行了簡單的翻譯.并作為資料與好友分享.考慮到國內(nèi)Orleans的框架文章并不是很多,所以想寫一些文章,來介紹一下它. ,本人不是什么大牛,只是肯花時(shí)間而已.一定有很多地方不完善,甚至是錯(cuò)誤的.能夠有資格”用”此框架的人,一定都是公司的技術(shù)骨干,所以我也有很多地方向大家學(xué)習(xí).如果誰想跟我交流聯(lián)系,歡迎加我QQ:82676624.
?一.鋪墊。
? 雖然是個(gè)入門例子,還是需要一些鋪墊。
?????? Orleans的最小完全體,應(yīng)該分為2個(gè)部分。一個(gè)是Orleans客戶端,一個(gè)是Orleans服務(wù)端,這里為什么要加上“Orleans”這個(gè)限定詞語呢?那是因?yàn)镺rleans的完全體,才是普通意義上的服務(wù)端主程。它們共同構(gòu)成了游戲服務(wù)器,網(wǎng)站服務(wù)器等等。
在Orleans客戶端中,我們使用GrainClient類以及Grain類,在Orleans服務(wù)端內(nèi),我們主要使用silo類和grain類。這里要說說Silo類,前面說過Grain類是處于“單線程機(jī)制”約束下的類,那么它們運(yùn)行在哪個(gè)地方呢?就是Silo類所在的地方。Silo類是Grain地代碼實(shí)際執(zhí)行的地方,它是Grain類的宿主,它承載著所有的Grain實(shí)例,也許是幾百萬個(gè)Grain實(shí)例。在一些語境下,silo,silohost,以及Orleans服務(wù)端,這三個(gè)詞語有可能代表同一個(gè)意思.
一個(gè)外部請求的處理大部分情況下需要很多個(gè)Grain實(shí)例,進(jìn)行一連串的方法調(diào)用后才能處理完畢,這些Grain實(shí)例形成一個(gè)處理消息的鏈條,這個(gè)消息流走于Grain鏈內(nèi)直至處理完畢。那么這個(gè)消息是如何第一次到達(dá)Grain鏈條里呢?GrainClient類的作用就是入口,它通知Silo類,有新消息達(dá)到,它需要哪個(gè)Grain類實(shí)例,需要調(diào)用特定的方法等。
?????? 經(jīng)過以上鋪墊,要想創(chuàng)造一個(gè)Orleans完全體,我來用vs2017創(chuàng)建一個(gè)解決方案,里面添加4個(gè)項(xiàng)目:Client,Host,Grains,IGrains。它們的作用分別解釋如下:
Client:這個(gè)顯而易見,里面就是要運(yùn)行GrainClient的。它要和Host通信,這就要求它引用IGrains項(xiàng)目。這是個(gè)控制臺項(xiàng)目
Host:這個(gè)也是顯而易見,里面就是要運(yùn)行Silo的。它應(yīng)該引用Grains項(xiàng)目以及IGrains項(xiàng)目,因?yàn)樗休dGrain(這就要求引用Grains類),并且需要Grain實(shí)例間的通信(這就要求引用IGrains項(xiàng)目),這是個(gè)控制臺項(xiàng)目
Grains:這個(gè)里面實(shí)現(xiàn)所有IGrain載明的接口,實(shí)現(xiàn)所有的Grain類,包括它們的方法以及字段。(它要求引用IGrain。。。廢話)這是個(gè)類庫項(xiàng)目
IGrains:這里放置所有Grains類要擴(kuò)展的接口。這是個(gè)類庫項(xiàng)目。
同時(shí)為了使用Orleans框架,我們還需要引用Orleans的類庫,官方教程里有方案,不過,我們采用簡單的辦法,統(tǒng)一引用一個(gè)類庫集合,下文有述。
好吧,以上解釋不管懂與不懂,你就暫且記下,先按照步驟一步一步來吧。也許某個(gè)階段,你就突然懂了。我相信在讀的各位對這種醍醐灌頂?shù)母杏X一定不陌生。
?
按照以上套路,我開始了我的工作,我打算跟世界say Hi:
二.創(chuàng)建
我創(chuàng)建了四個(gè)項(xiàng)目的解決方案,它長成這個(gè)樣子,注意它們的.net框架版本都是4.6.1:
?
?
好了我們要引用Orleans類庫啦,說了半天,終于要主角登場啦,好激動(dòng),所以我nuget引用了Microsoft.Orleans.Server,它長這樣子:
?
?
里面沒有實(shí)際內(nèi)容,我現(xiàn)在先弄IGrains,它很簡單,就一個(gè)接口一個(gè)方法:長成這樣:
?
?
我再弄Grains項(xiàng)目:它就是要實(shí)現(xiàn)IGrains項(xiàng)目里的接口,所以先引用哪個(gè)項(xiàng)目,然后實(shí)現(xiàn)它,長這樣子。
?
?
我再實(shí)現(xiàn)Client項(xiàng)目,它需要引用IGrains項(xiàng)目,它長這樣子:
?
?
最后是Host類,它長這樣子:
?
?
?
現(xiàn)在我高興的運(yùn)行這個(gè)Orleans完全體,它再一連串的日志記錄之后,跳出了 hello world如下圖
?
?
…我成功地創(chuàng)造了Orleans地完全體。好了全系列文章到此結(jié)束。
?補(bǔ)充
噢,我還需要補(bǔ)充一下下。
搞了半天,鬧這么大動(dòng)靜,就寫了個(gè)簡版的WCF?并不是,這里只是展示了碼Orleans的主要步驟。這里有幾個(gè)要點(diǎn)需要解釋一下:
?
| 1.IGrains的所有接口方法,必須是返回Task類的。這個(gè)是必須的,因?yàn)镺rleans的“單線程機(jī)制”,是建立在Task類之上的。它是利用TaskScheduler類,實(shí)現(xiàn)“單線程機(jī)制”的。啥?有人問怎么實(shí)現(xiàn)的?你確信自己入門教程還沒有看完就想知道?…以后再說吧。反正接口的方法,必須是返回Task。 |
2.IGrains接口擴(kuò)展自IGrainWithInteger接口,這個(gè)IGrainWithInteger接口是Orleans提供的,之前說過,Grain實(shí)例之間需要通信,這就需要彼此區(qū)分,要有各自的主標(biāo)識。這個(gè)接口,就規(guī)定了主標(biāo)識是長整數(shù)(名字是整數(shù),但是實(shí)際上是長整數(shù))。Grain實(shí)例的主標(biāo)識還可以是其他類型:比如字符串和Guid等等。 |
| 3.Grains項(xiàng)目中,實(shí)現(xiàn)了我規(guī)定的接口,就表明自己的主標(biāo)識是長整數(shù),它又?jǐn)U展自Grain類,就表明自己受“單線程機(jī)制”約束了 |
| 4.Host項(xiàng)目,只是引用了Grains項(xiàng)目,但是沒有再代碼中具體使用。它如何知道自己要承載哪些Grain類?Host利用反射檢查所有目錄里的類庫文件,如果找到grain類,它就加載,并管理起來 |
| 5.GrainClient有靜態(tài)的,有動(dòng)態(tài)的類,我這里先使用靜態(tài)類講解。動(dòng)態(tài)類其實(shí)一樣。我調(diào)用的時(shí)候,使用一個(gè)參數(shù)“12345”。就是要告訴Host。我要激活主標(biāo)識是12345的Grain類實(shí)例。然后又調(diào)用了它的say方法。它聰明的再host的控制臺窗口打印出say hello。 |
?
到現(xiàn)在,我們就明白了,再Orleans的世界里,給grain發(fā)送消息,實(shí)質(zhì)上是調(diào)用對應(yīng)的接口,grain實(shí)例接收到消息以后,使用對應(yīng)的方法進(jìn)行處理。(吃瓜群眾:這不就是一個(gè)RPC框架嘛…呵呵。)
在這個(gè)簡單的例子里,展現(xiàn)出的東西還有很多,不過今天很晚了,打完收工。明天繼續(xù)說說這個(gè)例子展現(xiàn)的內(nèi)容。
相關(guān)文章:?
.NET的Actor模型:Orleans
微軟分布式云計(jì)算框架Orleans(1):Hello World
微軟分布式云計(jì)算框架Orleans(2):容災(zāi)與集群(1)
Aaron Stannard談Akka.NET 1.1
使用Akka.net開發(fā)第一個(gè)分布式應(yīng)用
原文地址:http://www.cnblogs.com/gaopang/p/7379802.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的Orleans入门例子的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关系型数据库的分片原则
- 下一篇: Orleans例子再进一步