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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM