BeetleX之XRPC使用詳解
XRPC
是基於BeetleX
擴充套件一個遠端介面呼叫元件,它提供基於介面的方式來實現遠端服務呼叫,在應用上非常簡便。元件提供.NETCore2.1
和.NETStandard2.0
的client版本,因此即使在winfrom
和wpf
也可以使用該元件進行服務呼叫處理。接下來詳細講解一下XRPC
使用,從簡單的hello
到桌面wpf
呼叫服務、ssl通訊安全和物件注入等功能。
引用元件
元件提供了兩個版本BeetleX.XRPC
對應.NETCore2.1
它同時提供服務和客戶端呼叫功能,BeetleX.XRPC.Clients
是對應Standard2.0
客戶端版本,專門針對桌面應用呼叫而開發。除了這兩個元件外還提供了BeetleX.XRPC.Hosting
XRPC
服務提供以Hosting
方式執行的支援,如果你想使用DI
那也可以通過這個元件實現。
Hello
很多程式的開始都是以Hello
來展示使用,接下來就使用元件構建一個Hello
的通訊服務。元件的所有服務都需要通過介面來描述,所以在制定服務前需要用介面來描述一下服務需求:
public interface IHello { Task<string> Hello(string name); }
以上是一個Hello
服務介面的定義(介面定義要求是所有方法都必須以Task<T>
或Task
作為返回值)。服務實現
[Service(typeof(IHello))] public class HelloImpl : IHello { public Task<string> Hello(string name) { return $"hello {name} {DateTime.Now}".ToTask(); } }
以上是實現服務細節,接下來通過以下程式碼啟動服務:
static void Main(string[] args) { var builder = new HostBuilder() .ConfigureServices((hostContext, services) => { services.UseXRPC(s => { s.ServerOptions.LogLevel = BeetleX.EventArgs.LogType.Trace; s.ServerOptions.DefaultListen.Port = 9090; s.RPCOptions.ParameterFormater = new JsonPacket();//default messagepack }, typeof(Program).Assembly); }); builder.Build().Run(); }
以上是在所有IP.Any
上的9090
埠提供服務。接下來的工作就是如何呼叫它,XRPC
在使用上設計非常方便,所以在呼叫上會變得非常簡單.
client = new XRPCClient("localhost", 9090); client.Options.ParameterFormater = new JsonPacket();//default messagepack hello = client.Create<IHello>(); while (true) { Console.Write("Enter you name:"); var name = Console.ReadLine(); var result = await hello.Hello(name); Console.WriteLine(result); }
只需要指定XRPCClient
對應的服務地址和埠,並建立介面即可呼叫。XRPCClient
和它建立的介面都是執行緒安全的,因此只需要定義一個即可在併發中使用。
引數編碼
元件提供json
和messagepack
作為引數傳遞的編碼,messagepack
是預設編碼使這種編碼序列化物件可以達到非常好的效率,但這種編碼需要對類的屬性進行標記使用也相對麻煩;如果對效率要求不高不想對類進行屬性標記可以設定成Json
.如果想實現自己的編碼方式可以通過實現以下介面:
public interface IParameterFormater { void Encode(Options rpcOption, object data, PipeStream stream); object Decode(Options rpcOption, Type type, ArraySegment<byte> data); }
建立Actor
模式物件
Actor
是一種非常高效的業務處理模型,每個例項有著獨立執行緒資源,其行所有為是序列操作,所以它這種執行緒獨立性和無鎖機制非常適合高併發業務處理;XPRC
支援遠端Actor
建立,並在服務端維持其獨立性,在多客戶端同時呼叫同一Actor
行為時服務端會保證其自有的特性執行。
public interface IAmount { Task Income(int value); Task Pay(int value); Task<int> GetValue(); }
以上是一個簡單的數量增加介面,實現的服務如下:
[Service(typeof(IAmount))] public class AmountImpl : IAmount { private int mAmount; public Task<int> GetValue() { return mAmount.ToTask(); } public Task Income(int value) { mAmount -= value; return Task.CompletedTask; } public Task Pay(int value) { mAmount += value; return Task.CompletedTask; } }
元件在actor
應用並沒有特殊的要求,主要是客戶端在建立的時候告訴服務端需要建立一個指標識的actor
例項即可,程式碼如下:
client = new XRPCClient("localhost", 9090); client.Options.ParameterFormater = new JsonPacket();//default messagepack henry = client.Create<IAmount>("henry"); ken = client.Create<IAmount>("ken");
以上是針對IAmount
建立兩個例項,分別是:henry
和ken
.服務端會根據請求的標識在服務端維護各自的actor
例項。多客戶端同時建立相同名稱的actor
例項怎辦?即是多客戶端同時建立同一名稱的actor
和併發呼叫,服務端都可保證actor
例項的唯一性(實際應用需要涉及到actor
的狀態,資訊持久化等,這些就不在這裡討論;XRPC
的這一功能則由https://github.com/IKende/EventNext 提供)。
在WPF中呼叫
有時候需要在winfrom
或wpf
中呼叫服務,這個時候就需要通過BeetleX.XRPC.Clients
來實現呼叫;它所提供的功能和BeetleX.XRPC
內建的客戶端功能是一樣的。接下來做一個簡單的資料查詢,不過這個示例為了符合客戶端的需求還針對方法添加了JWT
驗證的功能。
public interface IDataService { Task<string> Login(string name, string pwd); Task<List<Employee>> List(); }
以上是一個簡單的資料查詢介面,裡面添加了一個登陸方法.
[Service(typeof(IDataService))] [TokenFilter] public class DataServiceImpl : IDataService { public Task<List<Employee>> List() { return DataHelper.Defalut.Employees.ToTask(); } [SkipActionFilter(typeof(TokenFilter))] public Task<string> Login(string name, string pwd) { string token = null; if (name == "admin" && pwd == "123456") token = JWTHelper.Default.CreateToken(name, "admin"); return token.ToTask(); } }
以上是對應的服務實現,但這個服務多了個TokenFilter
屬性;這個屬性是一個過慮器用於驗證請求的,Login
方法就移走了這個驗證過慮器。接下來看來下這個屬性的程式碼:
public class TokenFilter : ActionFilterAttribute { public override bool Executing(EventCenter center, EventActionHandler handler, IEventInput input, IEventOutput output) { string token = null; input.Properties?.TryGetValue("token", out token); var user = JWTHelper.Default.GetUserInfo(token); if (user!=null) { return base.Executing(center, handler, input, output); } else { output.EventError = EventError.InnerError; output.Data = new object[] { "操作錯誤,無權操作相關資源!" }; return false; } } }
過慮器邏輯比較簡單就是獲取請求頭的token
屬性是否有效,如果有則通過請求沒有則拒絕請求。接下來看一下WPF
的使用程式碼:
private IDataService dataService; private XRPCClient XRPCClient; private void Window_Loaded(object sender, RoutedEventArgs e) { XRPCClient = new XRPCClient("localhost", 9090); XRPCClient.Options.ParameterFormater = new JsonPacket(); dataService = XRPCClient.Create<IDataService>(); } private async void CmdSearch_Click(object sender, RoutedEventArgs e) { try { var data = await dataService.List(); lstEmployees.ItemsSource = data; } catch (Exception e_) { MessageBox.Show(e_.Message); } } private async void CmdLogin_Click(object sender, RoutedEventArgs e) { try { var token = await dataService.Login(txtName.Text, txtPwd.Text); txtToken.Content = token; ((IHeader)dataService).Header["token"] = token; } catch(Exception e_) { MessageBox.Show(e_.Message); } }
程式碼其實很簡單,在窗體構建的時候建立一個XRPCClient
並建立對應的介面例項;在這裡這裡主要是關心token
的傳遞,因為介面上並沒有方法可以這樣做;其實所有代理介面都實現了一個IHeader
介面,只需要做一個顯式的轉換並在Header
上設定對應名稱的值即可.
ssl的支援
安全的通訊在服務互動中是必不可少的,XRPC
通過支援ssl
來解決這一問題;由於這功能是BeetleX
的基礎核心,所以元件不需要太過於關注只需要簡單配置一下證書即可:
static void Main(string[] args) { var builder = new HostBuilder() .ConfigureServices((hostContext, services) => { services.UseXRPC(s => { JWTHelper.Init(); s.ServerOptions.LogLevel = BeetleX.EventArgs.LogType.Trace; s.ServerOptions.DefaultListen.Port = 9090; s.ServerOptions.DefaultListen.SSL = true; s.ServerOptions.DefaultListen.CertificateFile = "test.pfx"; s.ServerOptions.DefaultListen.CertificatePassword = "123456"; s.RPCOptions.ParameterFormater = new JsonPacket();//default messagepack }, typeof(Program).Assembly); }); builder.Build().Run(); }
只要在服務中配置好證書和對應的密碼即可,服務在啟動的時候會看到具體的情況:
服務啟用ssl
後,客戶端在建立XRPCClient
指定sslServiceName
即可,程式碼如下:
XRPCClient = new XRPCClient("localhost", 9090, "test"); XRPCClient.CertificateValidationCallback = (s, certificate, chain, sslPolicyErrors) => true; XRPCClient.Options.ParameterFormater = new JsonPacket(); dataService = XRPCClient.Create<IDataService>();
當無法確定sslServiceName
的時候需要新增CertificateValidationCallback
委託自己定義驗證返回的結果。
物件注入
由於服務物件的建立是由元件託管的,所以很多時候需要對介面服務新增不同的引數和屬性方便功能整合。使用者可以通過以下事件來服務類建立:
XRPCServer.EventCenter.ServiceInstance
其實元件提供了BeetleX.XRPC.Hosting
擴充套件來成這功能,通過這個擴充套件在服務啟動時進行註冊,程式碼如下:
var builder = new HostBuilder() .ConfigureServices((hostContext, services) => { services.AddSingleton(new User { Name = "BeetleX" }); services.UseXRPC(s => { //... }, typeof(Program).Assembly); }); builder.Build().Run();
通過services
可以給DI容器添注入需要的物件,當容器註冊後就可以在服務類的構建函式中定義需要的引數:
[Service(typeof(IHello))] public class HelloImpl : IHello { public HelloImpl(BeetleX.XRPC.XRPCServer server, User user) { mServer = server; mUser = user; } }
以上講述了XRPC
的使用情況,詳細程式碼可以訪問 https://github.com/IKende/BeetleX-Sam