BeetleX之XRPC遠程委託調用

BeetleX.XRPC是基於接口的遠程通訊組件,它不緊可以把接口提供客戶端調用,同樣也支持服務端創建客戶端的接口實例並主動調用客戶端的方法.接口有着非常的規範性和約束性,但前提你是必須制定相應的接口並實現才行;為了讓通訊在.NET平台使用變得更簡便,在新版中組件支持遠程委託調用.這功能不僅可以讓客戶端調用服務端的委託,同樣也可以讓服務端調用客戶端的委託.

簡介

組件支持任何委託的定義和調用包括框架集成的Action<T...>,Fun<T...,Result>和自定義委託.為了更好地滿足通訊上的需求還是有一些簡單的規規則約束;主要限制有:參數暫不支持refout,參數類型不能為Object因為無法進行反序列化處理.返回值必須是Task,Task<T>Void;為了在IO處理上更好地配合async/await來提高性能,組件要求返回值必須是TaskTask<T>,組件之所以支持Void主要是用於一些特別的場景,當委託為Void時是不會理會對端處理的情況(即發送後不管模式).還有組件對委託的參數也有限制,最大不能超過10個參數.

註冊委託

組件提供了一致的方式來進行委託註冊,方法如下:

AddDelegate<T>(T handler) where T : Delegate

註冊方法是可以任意委託類型和對應的方法

//客戶端  mClient = new XRPCClient("localhost", 9090);  mClient.Options.ParameterFormater = new JsonPacket();  mClient.AddDelegate<Action<DateTime>>(SetTime);  //服務端  mServer.AddDelegate<ListEmployees>(() => Task.FromResult(DataHelper.Defalut.Employees));  mServer.AddDelegate<ListCustomers>(() => Task.FromResult(DataHelper.Defalut.Customers));

在綁定委託可以指定類函數也可以是匿名函數;當註冊委託後對端就可以使用相同類型的Delegate進行代理和調用.

創建委託並調用

組件同樣提供一致的方式來創建代理和調用

//客戶端  mClient.Delegate<ListEmployees>()();  //服務端  mServer.Delegate<Action<DateTime>>(session)(DateTime.Now);

組件通過Delegate方法來創建相應委託代理,不過服務端在創建的時候必須指定客戶端的session對象,創建委託後就可以直接調用.組件針緩存創建的委託代理,即使頻繁地調用Delegate方法來創建也不用擔心在創建過程中帶來的損耗問題.

完整示例

上面已經描述了如何註冊和對調用的使用,接下來做一個完全的數據查詢示例來展示一下基於遠程委託調用的便利性,為了滿足這個示例的要求自定義了以下三個委託:

    public delegate Task<List<Order>> ListOrders(int employee, string employeeid);        public delegate Task<List<Employee>> ListEmployees();        public delegate Task<List<Customer>> ListCustomers();

這三個委託分別是:僱員,客戶和訂單查詢.接下來就定義一個WPF的客戶端程序通過調用這三個委託來進行數據查詢的操作:

public partial class MainWindow : Window  {      public MainWindow()      {          InitializeComponent();      }        XRPCClient mClient;        private async void Window_Loaded(object sender, RoutedEventArgs e)      {          mClient = new XRPCClient("localhost", 9090);          mClient.Options.ParameterFormater = new JsonPacket();          mClient.AddDelegate<Action<DateTime>>(SetTime);          comboEmployees.ItemsSource = from a in await mClient.Delegate<ListEmployees>()() select new { a.EmployeeID, Name = $"{a.FirstName} {a.LastName}" };          comboxCustomer.ItemsSource = await mClient.Delegate<ListCustomers>()();          lstOrders.ItemsSource = await mClient.Delegate<ListOrders>()(0, null);      }        private void SetTime(DateTime time)      {          this.Dispatcher.BeginInvoke(new Action<DateTime>(t =>          {              this.txtTime.Content = t.ToString();          }), time);      }        private async void CmdSearch_Click(object sender, RoutedEventArgs e)      {          lstOrders.ItemsSource = await mClient.Delegate<ListOrders>()(              comboEmployees.SelectedValue != null ? (int)comboEmployees.SelectedValue : 0,              comboxCustomer.SelectedValue != null ? (string)comboxCustomer.SelectedValue : null              );      }  }

為了展示服務端遠程調客戶端的,這裡註冊了一個Action<DateTime>用於服務端主動設置客戶端時間的方法.

static void Main(string[] args)  {      var builder = new HostBuilder()          .ConfigureServices((hostContext, services) =>          {              services.UseXRPC(s =>              {                  s.ServerOptions.LogLevel = LogType.Warring;                  s.RPCOptions.ParameterFormater = new JsonPacket();              },              c => {                  c.AddDelegate<ListEmployees>(() => Task.FromResult(DataHelper.Defalut.Employees));                  c.AddDelegate<ListCustomers>(() => Task.FromResult(DataHelper.Defalut.Customers));                  c.AddDelegate<ListOrders>((emp, cust) =>                  {                      Func<Order, bool> filter = (o) => (emp == 0 || o.EmployeeID == emp) && (String.IsNullOrEmpty(cust) || o.CustomerID == cust);                      return Task.FromResult((from a in DataHelper.Defalut.Orders where filter(a) select a).ToList());                  });                  Task.Run(() =>                  {                      while (true)                      {                          foreach (var item in c.Server.GetOnlines())                          {                                c.Delegate<Action<DateTime>>(item)(DateTime.Now);                              System.Threading.Thread.Sleep(1000);                          }                      }                  });                },              typeof(Program).Assembly);          });      builder.Build().Run();  }

以上是服務端的代碼,註冊了對應數據查詢的委託,並開啟一個簡單的定時任務每秒中向所有客戶端發送當前時間信息.接下來可以啟動服務端和客戶端運行結果如下:

從以上示例中可以發現,如果簡單的數據傳輸處理,那用委託進行一個約束使用起的確是簡便一些,畢竟.Net內置了一些委託類型可供使用無須自己定義,不過從應用規範上來說定義具體名稱的委託或用接口來制定調用規範還是很有必要的. 如果你想獲取完全示例可以訪問: 

https://github.com/IKende/BeetleX-Samples/tree/master/XRPC.DelegateInvoke

總結

.net core對wcf一直沒有更好地支持,開發XRPC希望能在通訊實現這樣一個類似的功能,現有版本的XRPC有.net core和std2.0 client.所以可以在.netcore,winform,wpf和支持std2.0的環境中應用.