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-Samples