基于.netcore 开发的轻量Rpc框架
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、其它目前还没有想到,如果各位博友有什么建议可以提一下,帮助我一下,谢谢
项目源码地址
今天晚上在写这篇随笔的时候发现自己无从下手,博友有没有支招的啊,非常感谢。
作者:yjq
欢迎任何形式的转载,但请务必注明出处。
.netcore相关功能需要定制的可以找我。有丰富的并发处理,性能优化经验。单点登录,限流,熔断,读写分离等功能均可定制。也可以帮忙系统优化处理,系统诊断,请联系博主(备注添加原因)。微信:yjq425527169 QQ:425527169
本篇文章如有些许帮助请点击推荐让更多需要帮助的人可以看到,请支持原创,请大方打赏(右边点击打赏)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?