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、其它目前还没有想到,如果各位博友有什么建议可以提一下,帮助我一下,谢谢
项目源码地址
今天晚上在写这篇随笔的时候发现自己无从下手,博友有没有支招的啊,非常感谢。