ApiClient搭配ApiServer构成RPC框架。
Nuget包:NewLife.Remoting
源码地址:https://github.com/NewLifeX/NewLife.Remoting/blob/master/NewLife.Remoting/ApiClient.cs
建议提前阅读《RPC通信ApiServer》。
快速入门
新建控制台项目,写入以下代码:
using NewLife.Data; using NewLife.Log; using NewLife.Net; using NewLife.Remoting; using System; using System.Linq; using System.Net; namespace NewLife.RPC { class Program { static void Main(string[] args) { XTrace.UseConsole(); var netUri = new NetUri(NetType.Tcp, IPAddress.Any, 5001); using var server = new ApiServer(netUri) { Log = XTrace.Log, EncoderLog = XTrace.Log, ShowError = true }; server.Register<BigController>(); server.Start(); ClientTest(); Console.ReadKey(); } static void ClientTest() { var client = new MyClient("tcp://127.0.0.1:5001") { Log = XTrace.Log, EncoderLog = XTrace.Log }; var rs = client.Invoke<Int32>("Big/Sum", new { a = 123, b = 456 }); XTrace.WriteLine("{0}+{1}={2}", 123, 456, rs); } class MyClient : ApiClient { public MyClient(String urls) : base(urls) { } protected override void OnReceive(IMessage message) { if (Encoder.Decode(message, out var action, out _, out var args)) { XTrace.WriteLine("通知:{0} 参数:{1}", action, args.ToStr()); } } } class BigController : IApi { public IApiSession Session { get; set; } public int Sum(int a, int b) { Task.Run(async () => { await Task.Delay(1000); Session.InvokeOneWay("test", new { name = "Stone", company = "NewLife" }, 3); }); return a + b; } public string ToUpper(string str) => str.ToUpper(); public Packet Test(Packet pk) { var buf = pk.ReadBytes().Select(e => (Byte)(e ^ 'x')).ToArray(); return buf; } } } }
这里的重点是客户端调用:client.Invoke<Int32>("Big/Sum", new { a = 123, b = 456 })
新生命RPC框架的客户端调用,只有一个 Invoke (异步InvokeAsync),指定接口名并传入参数对象即可。这就是客户端的全部,没有描述文件生成代码,也不需要客户端服务端共用接口文件!
当然,只要你喜欢,可以搞一套接口,让客户端和服务端控制器都实现。这么做,比较麻烦的地方在于修改接口时需要同时改几个地方。客户端服务端还可以共享出入参模型类,这也是常见做法。由于默认采用Json序列化,因此两头可以使用各自的模型类,只要字段名对得上即可,完全避免了客户端服务端升级版本不匹配所带来的各种问题。
下行通知
修改前面的示例,加上服务端主动下发代码:
class BigController : IApi { public IApiSession Session { get; set; } public int Sum(int a, int b) { Task.Run(async () => { await Task.Delay(1000); Session.InvokeOneWay("test", new { name = "Stone", company = "NewLife" }, 3); }); return a + b; } }
在Sum接口内,调用了 Session.InvokeOneWay。客户端也需要修改一下:
static void ClientTest() { var client = new MyClient("tcp://127.0.0.1:5001") { Log = XTrace.Log, EncoderLog = XTrace.Log }; var rs = client.Invoke<Int32>("Big/Sum", new { a = 123, b = 456 }); XTrace.WriteLine("{0}+{1}={2}", 123, 456, rs); } class MyClient : ApiClient { public MyClient(String urls) : base(urls) { } protected override void OnReceive(IMessage message) { if (Encoder.Decode(message, out var action, out _, out var args)) { XTrace.WriteLine("通知:{0} 参数:{1}", action, args.ToStr()); } } }
MyClient类继承自ApiClient,重写了 OnReceive 方法,所有未经处理的消息,都将进入这里。使用编码器解码后即可得到想要的数据。