原先做伺服器程式, 都是部署在xx雲上, 也沒理解雲是個啥, 不就是個伺服器(虛擬機)租賃商嗎? 好吧, 其實這個是IaaS, 而接下來要學習的ServiceFabric(以下簡稱SF)是PaaS. 首先SF和Orleans類似, 都是基於actor模型, 然後編程方式也很像, 大概就是定義公開介面 ...
原先做伺服器程式, 都是部署在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一樣, 普通的有狀態服務仍會非同步調用.