1. 程式人生 > >BeetleX之XRPC遠端委託呼叫

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的環境中應