Rpc原理詳解
博客上已經有人解釋的很詳細了,我就不在解釋了。傳送門
項目簡介
項目是依賴於.net core2.0版本,內部都是依靠IOC來實現的,方便做自定義擴展。底層的通信是采用socket,sokcet的代碼參考Enode的socket代碼。類的序列化目前只支持自帶的BinarySerializer和Json.net,也可以自定義,擴展也很方便。也支持zookeeper的服務協調。
框架傳輸及序列化邏輯
當客戶端發起請求時,根據建立的客戶端代理,獲取當前的請求方法信息(名字、所屬類型、參數值),通過自帶的BinarySerializer將其序列化,所以在傳輸的方法中的自定義的類就必須加上【Serializable】可序列化標記,不然會報錯。客戶端將請求的方法信息序列化成字節數組之后傳輸到服務端,服務端獲取到信息后根據方法信息獲取到要執行的方法,然后執行該方法,並將結果返回給客戶端。返回結果的序列化可以采用自定義的標記來進行,比如在方法或者類上面打上【BinarySerializer】標記,則采用BinarySerializer序列化,打上【JsonSerializer】標記則采用json.net來序列化話,后期可以支持protobuf來序列化。
服務端代碼
首先定義一個接口和一個實現類

namespace NetCoreRpc.Application { public interface IStudentApplication { int Age(); bool IsYongPeople(int age); void Say(string msg); Task Sleep(); Task<int> RunAsync(int sleepTime); void Say(byte[] msg); byte[] Say(); [BinarySerializer] TestModel Test(); } public class StudentApplication : IStudentApplication { public int Age() { return 10; } public bool IsYongPeople(int age) { return age < 18; } public async Task<int> RunAsync(int sleepTime) { await Task.Delay(sleepTime); return sleepTime; } public void Say(string msg) { Console.WriteLine($"Say:{msg}"); } public Task Sleep() { return Task.Delay(10); } public void Say(byte[] msg) { Console.WriteLine(Encoding.UTF8.GetString(msg)); } public byte[] Say() { return Encoding.UTF8.GetBytes("Good Job!"); } public TestModel Test() { return new TestModel { Age = 10, Msg = Encoding.UTF8.GetBytes("Hello") }; } } [Serializable] public class TestModel { public int Age { get; set; } public byte[] Msg { get; set; } public override string ToString() { return $"{Age}|{Encoding.UTF8.GetString(Msg)}"; } } }
不基於zookeeper的服務端版本

internal class Program { public static IConfigurationRoot Configuration; private static void Main(string[] args) { Console.WriteLine("請輸入監聽端口:"); var strPort = Console.ReadLine(); var builder = new ConfigurationBuilder(); //.SetBasePath(Path.Combine(AppContext.BaseDirectory)).AddJsonFile("NetCoreRpc.json", optional: true); Configuration = builder.Build(); var servicesProvider = BuildDi(); DependencyManage.SetServiceProvider(servicesProvider, Configuration); NRpcServer nrpcServer = new NRpcServer(int.Parse(strPort)); nrpcServer.Start("NetCoreRpc.Application"); Console.WriteLine("Welcome to use NetCoreRpc!"); Console.WriteLine("Input exit to exit"); var str = Console.ReadLine(); while (!string.Equals(str, "exit", StringComparison.OrdinalIgnoreCase)) { str = Console.ReadLine(); } nrpcServer.ShutDown(); } private static IServiceProvider BuildDi() { IServiceCollection services = new ServiceCollection(); services.AddSingleton<ILoggerFactory, LoggerFactory>(); services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); services.AddSingleton<IStudentApplication, StudentApplication>(); services.UseRpc(); //.UseZK(); var serviceProvider = services.BuildServiceProvider(); var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>(); loggerFactory.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true }); loggerFactory.ConfigureNLog("NLog.config"); return serviceProvider; } }
基於zookeeper的服務端版本

internal class Program { public static IConfigurationRoot Configuration; private static void Main(string[] args) { Console.WriteLine("請輸入監聽端口:"); var strPort = Console.ReadLine(); var builder = new ConfigurationBuilder() .SetBasePath(Path.Combine(AppContext.BaseDirectory)).AddJsonFile("NetCoreRpc.json", optional: true); Configuration = builder.Build(); var servicesProvider = BuildDi(); DependencyManage.SetServiceProvider(servicesProvider, Configuration); NRpcServer nrpcServer = new NRpcServer(int.Parse(strPort)); nrpcServer.Start("NetCoreRpc.Application"); Console.WriteLine("Welcome to use NetCoreRpc!"); Console.WriteLine("Input exit to exit"); var str = Console.ReadLine(); while (!string.Equals(str, "exit", StringComparison.OrdinalIgnoreCase)) { str = Console.ReadLine(); } nrpcServer.ShutDown(); } private static IServiceProvider BuildDi() { IServiceCollection services = new ServiceCollection(); services.AddSingleton<ILoggerFactory, LoggerFactory>(); services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); services.AddSingleton<IStudentApplication, StudentApplication>(); services.UseRpc() .UseZK(); var serviceProvider = services.BuildServiceProvider(); var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>(); loggerFactory.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true }); loggerFactory.ConfigureNLog("NLog.config"); return serviceProvider; } }
客戶端代碼
首先要引用剛剛定義的接口和Model

internal class Program { public static IConfigurationRoot Configuration; private static void Main(string[] args) { var builder = new ConfigurationBuilder().SetBasePath(Path.Combine(AppContext.BaseDirectory)).AddJsonFile("NetCoreRpc.json", optional: true); Configuration = builder.Build(); var servicesProvider = BuildDi(); DependencyManage.SetServiceProvider(servicesProvider, Configuration); Console.WriteLine("Welcome to use NetCoreRpc!"); var studentApplication = ProxyFactory.Create<IStudentApplication>(); Console.WriteLine(studentApplication.Age()); Console.WriteLine(studentApplication.IsYongPeople(15)); var runTask = studentApplication.RunAsync(111); studentApplication.Say("Hello world"); studentApplication.Say(Encoding.UTF8.GetBytes("Hi!")); Console.WriteLine(Encoding.UTF8.GetString(studentApplication.Say())); var test = studentApplication.Test(); Console.WriteLine(test.ToString()); studentApplication.Sleep(); Console.WriteLine(runTask.Result); Console.WriteLine("Input exit to exit"); var str = Console.ReadLine(); while (!string.Equals(str, "exit", StringComparison.OrdinalIgnoreCase)) { str = Console.ReadLine(); } } private static IServiceProvider BuildDi() { IServiceCollection services = new ServiceCollection(); services.AddOptions(); services.Configure<RemoteEndPointConfig>(Configuration.GetSection("NetCoreRpc")); services.AddSingleton<ILoggerFactory, LoggerFactory>(); services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); services.UseRpc().UseZK(); var serviceProvider = services.BuildServiceProvider(); var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>(); //configure NLog loggerFactory.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true }); loggerFactory.ConfigureNLog("NLog.config"); return serviceProvider; } }

{ "NetCoreRpc": { "Default": "192.168.129.194:12346,192.168.129.194:12347,192.168.129.194:12348", "Group": [ { "NameSpace": "", "Address": "127.0.0.1:12345" } ], "Zookeeper": { "Connection": "192.168.100.34:2181", "ParentName": "/NetCoreRpc/ClientTest" } } }

internal class Program { public static IConfigurationRoot Configuration; private static void Main(string[] args) { var builder = new ConfigurationBuilder().SetBasePath(Path.Combine(AppContext.BaseDirectory)).AddJsonFile("NetCoreRpc.json", optional: true); Configuration = builder.Build(); var servicesProvider = BuildDi(); DependencyManage.SetServiceProvider(servicesProvider, Configuration); Console.WriteLine("Welcome to use NetCoreRpc!"); var studentApplication = ProxyFactory.Create<IStudentApplication>(); Console.WriteLine(studentApplication.Age()); Console.WriteLine(studentApplication.IsYongPeople(15)); var runTask = studentApplication.RunAsync(111); studentApplication.Say("Hello world"); studentApplication.Say(Encoding.UTF8.GetBytes("Hi!")); Console.WriteLine(Encoding.UTF8.GetString(studentApplication.Say())); var test = studentApplication.Test(); Console.WriteLine(test.ToString()); studentApplication.Sleep(); Console.WriteLine(runTask.Result); Console.WriteLine("Input exit to exit"); var str = Console.ReadLine(); while (!string.Equals(str, "exit", StringComparison.OrdinalIgnoreCase)) { str = Console.ReadLine(); } } private static IServiceProvider BuildDi() { IServiceCollection services = new ServiceCollection(); services.AddOptions(); services.Configure<RemoteEndPointConfig>(Configuration.GetSection("NetCoreRpc")); services.AddSingleton<ILoggerFactory, LoggerFactory>(); services.AddSingleton(typeof(ILogger<>), typeof(Logger<>)); services.UseRpc();//.UseZK(); var serviceProvider = services.BuildServiceProvider(); var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>(); //configure NLog loggerFactory.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true }); loggerFactory.ConfigureNLog("NLog.config"); return serviceProvider; } }
NetCoreRpc.json中的Zookeeper節點可以不用配置
調用測試結果
服務端輸出如下:
客戶端輸出如下:
項目中感覺不足之處
1、傳輸時采用的序列化采用的是自帶的BinarySerializer,需要在每個Model打上可序列化標記,后期希望改成不需要打標記就可以序列化的
2、采用zookeeper時,獲取可用IP是獲取當前第一個可用的IP,沒有有任何的算法
3、其它目前還沒有想到,如果各位博友有什么建議可以提一下,幫助我一下,謝謝
項目源碼地址
今天晚上在寫這篇隨筆的時候發現自己無從下手,博友有沒有支招的啊,非常感謝。