1. 程式人生 > >BeetleX之XRPC使用詳解

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-Sam