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的环境中应用.