原先做服務器程序, 都是部署在xx雲上, 也沒理解雲是個啥, 不就是個服務器(虛擬機)租賃商嗎? 好吧, 其實這個是IaaS, 而接下來要學習的ServiceFabric(以下簡稱SF)是PaaS.
首先SF和Orleans類似, 都是基於actor模型, 然后編程方式也很像, 大概就是定義公開接口, 然后后端服務實現接口, 前端調用接口這樣.
這里說的前后端着實不夠嚴謹, 其實是指服務器上的前后端, 前端是服務的前端並非服務器對應的客戶端. 如果有web(asp.net)或是其他無狀態類似網關的服務在前面接收客戶端消息並調用服務接口, 那就是服務的使用者也就是這里語義上的前端了(語死早).
服務根據其是否有狀態分為: 無狀態服務和有狀態服務.
無狀態服務很容易理解, 一個helloword, echo示例, 計算器示例, 只處理用戶輸入本身不保存狀態, 那就是無狀態服務. 無狀態服務的擴展很簡單, 增加節點既是.
然而如計算器這種服務, 如果需要保存一些數據等待用戶下次使用, 那就需要變成有狀態服務了. 當然也可以引入第三方存儲來規避狀態, 繼續使用無狀態服務(偽), 因為此時的狀態已經托付給第三方存儲了.
當然上述做法使得結構又復雜起來了, 也可能造成額外的調用等待, 不如使用有狀態服務.
而服務有了狀態, 那又涉及到狀態的落地, 容災, 同步, 鎖...這些東西煩擾了我整個編程生涯. 據說可以在SF中找到答案.
官網的文檔入門案例太復雜了, 引入了angular等加高門檻攪腦汁的東西, 而我今天不打算學太多, 先做一個helloworld消化下. 最后的功能是能通過一個console程序調用服務接口, 然后接收返回的字符串helloworld.
一 安裝必須工具
毫無疑問, visual studio是必須的, 我選擇目前最新的vs2017社區免費版.
然后安裝sdk
二 創建無狀態服務
打開vs->新建項目->Visual C#->cloud->Serivce Fabric Application, 名稱為HelloWorldApplication
選擇無狀態服務(stateless service), 名稱為HelloWorldService
三 定義接口
創建接口, IHelloWorldService.cs
定義方法Task SayHello(string msg);
為了能遠程調用, 使用nuget安裝包Microsoft.ServiceFabric.Services.Remoting
此時打開"編輯項目文件csproj", 會看到如下的包引用
<ItemGroup> <PackageReference Include="Microsoft.ServiceFabric.Services" Version="3.1.301" /> <PackageReference Include="Microsoft.ServiceFabric.Services.Remoting" Version="3.1.301" /> </ItemGroup>
編輯IHelloWorldService, 繼承自IService(在剛才安裝的remoting包中, 需要添加引用)
最后如下
using Microsoft.ServiceFabric.Services.Remoting; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace HelloWorldService { public interface IHelloWorldService : IService { Task<string> SayHello(string msg); } }
這里的返回值是Task類型. 老實說我初次接觸SF並不清楚其是否和Orleans一樣將消息接收和處理異步開來, 同時通過task的同步調用使服務處理不用擔心多線程問題. 但我猜自家產品沒有理由不采用同樣先進的方式吧?
四 實現接口
編輯HelloWorldService.cs文件, 繼承IHelloWorldService接口, 實現SayHello方法.
刪除原先存在的StartAsync方法. (Azure Cloud Service方式的延續?)
最后代碼如下
internal sealed class HelloWorldService : StatelessService, IHelloWorldService { public HelloWorldService(StatelessServiceContext context) : base(context) { } public Task<string> SayHello(string msg) { return Task.FromResult($"hello world! {msg}"); }/// <summary> /// 可選擇性地替代以創建偵聽器(如 TCP、HTTP),從而使此服務副本可以處理客戶端或用戶請求。 /// </summary> /// <returns>偵聽器集合。</returns> protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() { //return new ServiceInstanceListener[0]; return this.CreateServiceRemotingInstanceListeners(); } }
服務到此就創建完畢了.
五 發布到本地集群
右鍵HelloWorldApplication項目, 然后發布, 選擇LocalNode配置, 然后發布.
經歷漫長的等待之后, 右下角出現了一個SF的橘黃色圖標, 右鍵Manage local cluster, 可以查看和管理已經發布的applicaiton, 以及具體運行的node.
注意, 忘記說了, vs需要用管理員打開...
六 接下來創建客戶端.
新建項目, console application, 任意, 我選擇.net Core Console Application.
nuget獲取包
<ItemGroup> <PackageReference Include="Microsoft.ServiceFabric.Services" Version="3.1.301" /> <PackageReference Include="Microsoft.ServiceFabric.Services.Remoting" Version="3.1.301" /> </ItemGroup>
添加現有項, 將剛才的IHelloWorldService.cs添加進去(注意可以選擇添加鏈接文件保持同步)
保持接口同步的還有一個方法是另建一個類庫項目, 引用同一個類庫(如同Orleans示例中一樣)
修改Program中的main函數
static void Main(string[] args) {
var client = ServiceProxy.Create<IHelloWorldService>(new Uri("fabric:/HelloWorldApplication/HelloWorldService"));
string msg = Console.ReadLine();
var result = client.SayHello(msg).Result; Console.WriteLine(result); Console.ReadKey(); }
F5運行客戶端, 查看結果.
同上示例, 還可以拓展為計算器服務.
糾錯: 上面說接口返回是Task可能是因為內部維持序列同步調用的關系, 其實是錯誤的. SF還有actor模型, 才和Orleans一樣, 普通的有狀態服務仍會異步調用.