Orleans稍微複雜的例子—互動
當初阿爾法狗在圍棋上戰勝人類的時候,人工智慧一時大熱,不管老小,都大肆談論一番深度神經網路學習演算法等等,我當時也是一時興起去拿出我嶄新的書本,說起來都是淚,大學並沒有好好學習,都玩遊戲了…不過不管如何,數學系畢業的,底子好點,翻看完必要的知識後,就去看深度神經網路演算法,去了解它們的原理,看到最後,我發現,這不就是一種全分類演算法嗎?頓時覺得高大上的深度學習網路沒有那麼神秘了.經常看到這樣的圖片:
我們拿過來用,借用微軟的話:一個服務本質上是多個可通信actor的集合,用上面的圖片好像正合適.
Grain類是輕量級的,為了更好的設計,現實中往往使用grain類來代表顆粒度很細的東西.比如一個商城,可以用GrainA來代表店鋪,用GrainB來代表商品…商鋪管理商品,所以GrainB就會和GrainA之間有互動,在系統中,對每個商品進行單獨控制,就需要很多的GrainB的實例,這在Orleans中很容易做到,更難得的是,可以對GrainB或者GrainA發生的事件進行記錄,實現eventsourcing. Actor的建模方式,使得eventsourcing顯得很自然,也很容易處理.
讓多個Grain互動起來才有更多的可能性,一個服務本質上是多個可通信actor的集合.所以在這個例子中,我們創造多個Grain實例,讓它們彼此之間有聯繫.我們使用」現實中的經理於員工的關係」這樣一個背景模型來構造這個例子.
步驟
我把它們添加到basic項目里,介面如下圖
還是跟以前一樣,定義介面.這裡注意這些介面只有方法,盡量避免使用屬性.雖然現實當中一個經理同時也是一個員工,但是這裡的經理介面並沒有繼承自員工介面.這樣做是基於以下考慮:這兩個介面都要被擴展成Grain類,以後對員工和經理的狀態值進行持久化存儲的時候,」非繼承」的兩個grain類會帶來一些方便.稍微說一下持久化:意思就是對Grain類的各個欄位進行持久化存儲(存到資料庫或者文件都可以),方便下一次Grain激活的時候讀取各個欄位的值.
好了,不管如何,我就是這麼勤快的人,直接寫了兩個介面,而不是選擇繼承.現在我勤快的實現這兩個Grain類.
如下圖:
大部分基礎工作做完了,我們再client調用它們.
運行之後的截圖如下:
上個例子雖然正確的運行了,但是隱藏一個小坑,真正正確的操作應該如下圖
這是因為Grain的方法都是非同步的.如果不添加await,在一些耗時的操作中也許會出現先後順序的錯誤.不過對於這個例子,添加不添加與否,並不影響結果.
解釋一下
以前說過,Orleans是基於actor模型構建的,但是Orleans把actor模型做到了極致,它是虛擬的actor模型.一個grain的生命周期,是從一個激活開始,到一個反激活結束.它既然是」激活」而不是構造,這就要求grain類不應該有構造函數,即便是沒有參數的構造函數.一個grain從來不是 從構建到銷毀,而是處於激活或者非激活兩個狀態. 可以在OnActivateAsync()中控制Graini的激活行為,實現類似於」構造」的過程Orleans保證這個方法會在所有其他方法之前調用.在上一個例子中就展現了如何使用這個方法.
在多線程的編程中,最討厭的兩個事情是死鎖和錯誤處理.有時候,在開發的時候測試一百次都不會死鎖,但是到部署到客戶哪裡,一次就給你死鎖.這一般是因為測試的環境簡單化了,沒有考慮到客戶環境的並發量以及並發的時間點.更因為這兩個因素很多時候根本無法考慮.
消息死鎖
Orleans編程可以做到全程無鎖,它同時使得多線程編程邏輯上更容易處理,錯誤更容易捕捉.但是這不代表不需要設計.為了展現一下,我再此修改員工和經理.這次我們設計一個問候消息如下:
我設想,新員工加入的時候經理給一個問候,同時新員工回答thanks,這樣來來回回的消息,就像兩個乒乓球機器人一樣對發對打.加上一個count,本意是想著來回的次數不要超過3次.不然就進入了死循環了.
修改
IEmployee
中的Greeting方法的參數.修改對介面的實現.
也更新Manager類,這樣它就會發送一個新的消息.
現在經理說」Welcome to my team!" ,員工應該回復一個Thanks.
如果運行這個程序,就造成了一個死鎖,這是因為經理髮送消息給員工,並等待回應,員工回答Thanks,並等待經理回應.問題就在於員工的消息永遠得不到回答,因為經理正在忙著等待它第一個消息完成呢,在經理那裡,員工的回答必須排隊等待.這就是一個死鎖!!同樣的還有死循環.在Actor編程中死循環往往容易處理(在任何編程中都容易處理),因為它容易定位.但是死鎖就稍微困難點.
Orleans提供了日誌,可以記錄並提示可能的死鎖,它一般用WARNING 體現出來,結尾是About to break its promise. 注意在伺服器繁忙的時候,網路不好的時候,都有可能出現這個警告.
為了 應對這樣的死鎖,Orleans提供了一個特性[Reentrant],它可以標誌一個Grain,使得這個類在處理其他消息的時候,稍微富有」彈性」,可以同時接受其他新消息的到達,(只是」到達」這個步驟」彈性」化,」處理消息」這個步驟依然是剛性的」單線程約束」).使用方法很簡單.如下:
處理建議
Orleans系統分為客戶端和服務端.對於網站來說,Orleans客戶端通常是asp或者其他的http框架,而Orleans服務端通常是資料庫操作端等等.在一個中大型的Orleans系統中,多重Actor互相關聯,最好是自上而下的設計,遵循好消息流或者數據流的流動方向,讓它們的流動可以分支,但是小心處理迴流,(需要迴流的地方請小心設計,不過這種情況也不會很多,如果很多,請檢查自己的數據設計是否合理).
在用一個Grain去管理多個Grain類的想法中,還有一點要注意的是,盡量不要產生過於繁忙的Grain實例.由於Grain實例是」單線程機制」的,一個實例操作過於繁忙會影響效率.上例中,如果一個經理直接下屬有一百萬個人.那麼這樣設計的程序過熱點就是這個經理.此時需要分散過熱點.一個簡單的辦法是按照員工主標識hash表分段,每一段分給一個經理…此時組合主標識就會派上用場.
吃瓜群眾應該覺得此處應該有總結:
介面項目就是平時說的」消息協議」.發送消息就是調用介面.在方法內部可以不用鎖而隨意讀寫欄位.避免死鎖和過熱的grain實例.
上例中設計了單獨的問候類,在.net世界裡,在作為參數傳遞和使用的時候,.net是傳遞實例的地址.但是這種機制在並發和多線程的情況下,並不能很好的工作.在Orleans世界裡,作為消息的參數(即Grain方法的參數),傳遞的時候默認都是對類實例進行一個深度拷貝,在跨機器的情況下,這是很有必要的.但是如果消息發送的目的地就是在本地silo里,如果還是需要進行深度拷貝,就顯得多餘和浪費了.Orleans提供了一個特性[immutable],用它來標明一個類一旦建立就不會再對它進行修改.如果消息目的地是本地silo,使用[immutable]標明的類作為參數時,Orleans就會跳過對此參數的深度拷貝,而是只傳遞一個引用.使用非常簡單.如下:
好了,第二個例子就說完了.有點Orleans實際項目的影子了,但是還是不夠…
似乎應該更進一步,那就是存儲,把Grain的狀態值保存起來,下一次激活的時候,恢復它的狀態值,這樣才能符合現實需要.可以嘗試在OnActivateAsync里控制讀取動作,在OnDeactivateAsync中控制存儲動作.不過微軟提供了更為通用的存儲中間件(StorageProvider),使得這些工作變得容易.
文章摘自博客園
更多精彩推薦
海量IT視頻教程:http://xue.ujiuye.com/
IT必備小技巧:http://zhi.ujiuye.com/
零基礎學習IT技術:http://www.ujiuye.com/zt/qgjx?wt.bd=lsh11tt
※.net core 2.0學習筆記:開發運行環境搭建
※CSS規範——春風十里不如寫好CSS
※我們是誰?乙方!
※Python:數據模型和特殊方法(魔術方法)
※簡單了解Orleans
TAG:IT優就業 |