zoukankan      html  css  js  c++  java
  • BeetleX之XRPC使用详解

    XRPC是基于BeetleX扩展一个远程接口调用组件,它提供基于接口的方式来实现远程服务调用,在应用上非常简便。组件提供.NETCore2.1.NETStandard2.0的client版本,因此即使在winfromwpf也可以使用该组件进行服务调用处理。接下来详细讲解一下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和它创建的接口都是线程安全的,因此只需要定义一个即可在并发中使用。

    参数编码

    组件提供jsonmessagepack作为参数传递的编码,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创建两个实例,分别是:henryken.服务端会根据请求的标识在服务端维护各自的actor实例。多客户端同时创建相同名称的actor实例怎办?即是多客户端同时创建同一名称的actor和并发调用,服务端都可保证actor实例的唯一性(实际应用需要涉及到actor的状态,信息持久化等,这些就不在这里讨论;XRPC的这一功能则由https://github.com/IKende/EventNext 提供)。

    在WPF中调用

    有时候需要在winfromwpf中调用服务,这个时候就需要通过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

  • 相关阅读:
    执行.class文件
    Ant能干什么,编译?打包!
    C的随想
    微服务
    2018年宝鸡市高考复课报告会材料
    用图像解不等式
    高频易错题目01
    2018年宝鸡市二检数学题目解答
    点差法
    和事件的概率求法
  • 原文地址:https://www.cnblogs.com/smark/p/12133195.html
Copyright © 2011-2022 走看看