基于.NET CORE微服务框架 -谈谈surging 的messagepack、protobuffer、json.net 序列化
1、前言
surging内部使用的是高性能RPC远程服务调用,如果用json.net序列化肯定性能上达不到最优,所以后面扩展了protobuf,messagepack序列化组件,以支持RPC二进制传输.
在这里需要感谢白纸无字Zonciu,新增了messagepack序列化,让surging 性能上跨了一大步。此篇文章我们来谈谈messagepack、protobuffer、json.net ,并且性能做下对比
开源地址:https://github.com/dotnetcore/surging
2、序列化组件
2.1 surging 使用的是以下序列化组件:
json.net:surging 使用的是Newtonsoft.Json, 它是基于json格式的序列化和反序列化的组件.官方网站: http://json.codeplex.com/
protobuf:surging 使用的是protobuf-net, 它是基于二进制格式的序列化和反序列化的组件.官方网站: https://github.com/mgravell/protobuf-net
messagepack:surging 使用的是MessagePack-CSharp, 它是基于二进制格式的序列化和反序列化的组件.官方网站: https://github.com/neuecc/MessagePack-CSharp
2.2 各个组件的优点
json.net 有以下优点:
侵入性:可以不添加attribute,就能进行序列化操作
灵活性:可以灵活性配置,比如允许被序列化的成员自定义名字,屏蔽的非序列化属性成员
可读性: 数据格式比较简单, 易于读写
依赖性:可以序列化成JObject,无需依赖对象进行序列化和泛型化。
protobuf 有以下优点:
性能高 序列化后体积相比Json和XML很小,适合RPC二进制传输
跨语言:支持跨平台多语言
兼容性:消息格式升级和兼容性还不错
速度快 :序列化反序列化速度很快,快于Json的处理速速
messagepack有以下优点:
性能高 序列化后体积相比Json和XML很小,适合RPC二进制传输
跨语言:支持跨平台多语言
兼容性:消息格式升级和兼容性还不错
速度快 :序列化反序列化速度很快,快于Json的处理速度
针对于protobuf和messagepack都是基于二进制格式的序列化和反序列化,优点都一样,但是基于messagepack的MessagePack-CSharp组件侵入性更小,可以不需要加attribute,而且性能上更优.下一节来看看组件在surging 中的表现
3. 性能比较
服务端:
(注:如果不加UseProtoBufferCodec和UseMessagePackCodec就是json.net序列化)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | var host = new ServiceHostBuilder() .RegisterServices(option=> { option.Initialize(); //初始化服务 option.RegisterServices(); //依赖注入领域服务 option.RegisterRepositories(); //依赖注入仓储 option.RegisterModules(); //依赖注入第三方模块 option.RegisterServiceBus(); //依赖注入ServiceBus }) .RegisterServices(builder => { builder.AddMicroService(option => { option.AddServiceRuntime(); // // option.UseZooKeeperManager(new ConfigInfo("127.0.0.1:2181")); //使用Zookeeper管理 option.UseConsulManager( new ConfigInfo( "127.0.0.1:8500" )); //使用Consul管理 option.UseDotNettyTransport(); //使用Netty传输 option.UseRabbitMQTransport(); //使用rabbitmq 传输 option.AddRabbitMQAdapt(); //基于rabbitmq的消费的服务适配 // option.UseProtoBufferCodec();//基于protobuf序列化传输 option.UseMessagePackCodec(); //基于MessagePack序列化传输 builder.Register(p => new CPlatformContainer(ServiceLocator.Current)); //初始化注入容器 }); }) .SubscribeAt() //消息订阅 .UseServer( "127.0.0.1" , 98) //.UseServer("127.0.0.1", 98,“true”) //自动生成Token //.UseServer("127.0.0.1", 98,“123456789”) //固定密码Token .UseStartup<Startup>() .Build(); using (host.Run()) { Console.WriteLine($ "服务端启动成功,{DateTime.Now}。" ); } |
客户端:
var host = new ServiceHostBuilder() .RegisterServices(option => { option.Initialize(); option.RegisterServices(); option.RegisterRepositories(); option.RegisterModules(); }) .RegisterServices(builder => { builder.AddMicroService(option => { option.AddClient(); option.AddClientIntercepted(typeof(CacheProviderInterceptor)); //option.UseZooKeeperManager(new ConfigInfo("127.0.0.1:2181")); option.UseConsulManager(new ConfigInfo("127.0.0.1:8500")); option.UseDotNettyTransport(); option.UseRabbitMQTransport(); option.UseProtoBufferCodec(); //option.UseMessagePackCodec(); builder.Register(p => new CPlatformContainer(ServiceLocator.Current)); }); }) .UseClient() .UseStartup<Startup>() .Build(); using (host.Run()) { Startup.Test(ServiceLocator.GetService<IServiceProxyFactory>()); Startup.TestRabbitMq(); }
测试 0 object(注:测试无参数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /// <summary> /// 测试 /// </summary> /// <param name="serviceProxyFactory"></param> public static void Test(IServiceProxyFactory serviceProxyFactory) { Task.Run(async () => { var userProxy = serviceProxyFactory.CreateProxy<IUserService>( "User" ); await userProxy.GetUserId( "user" ); do { Console.WriteLine( "正在循环 1w次调用 GetUser....." ); //1w次调用 var watch = Stopwatch.StartNew(); for ( var i = 0; i < 10000; i++) { var a =userProxy.GetDictionary().Result; } watch.Stop(); Console.WriteLine($ "1w次调用结束,执行时间:{watch.ElapsedMilliseconds}ms" ); Console.ReadLine(); } while ( true ); }).Wait(); } |
测试 1 object(注:测试参数传对象)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /// <summary> /// 测试 /// </summary> /// <param name="serviceProxyFactory"></param> public static void Test(IServiceProxyFactory serviceProxyFactory) { Task.Run(async () => { var userProxy = serviceProxyFactory.CreateProxy<IUserService>( "User" ); await userProxy.GetUserId( "user" ); do { Console.WriteLine( "正在循环 1w次调用 GetUser....." ); //1w次调用 var watch = Stopwatch.StartNew(); for ( var i = 0; i < 10000; i++) { var a =userProxy.GetUser( new UserModel { UserId = 1 }).Result; } watch.Stop(); Console.WriteLine($ "1w次调用结束,执行时间:{watch.ElapsedMilliseconds}ms" ); Console.ReadLine(); } while ( true ); }).Wait(); } |
测试 10 object(注:测试参数传List 集合对象)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /// <summary> /// 测试 /// </summary> /// <param name="serviceProxyFactory"></param> public static void Test(IServiceProxyFactory serviceProxyFactory) { Task.Run(async () => { var userProxy = serviceProxyFactory.CreateProxy<IUserService>( "User" ); await userProxy.GetUserId( "user" ); var list = new List<UserModel>(); for ( int i=0;i<10;i++) { list.Add( new UserModel { UserId = 1, Age = 18, Name = "fanly" }); } do { Console.WriteLine( "正在循环 1w次调用 GetUser....." ); //1w次调用 var watch = Stopwatch.StartNew(); for ( var i = 0; i < 10000; i++) { var a =userProxy.Get(list).Result; } watch.Stop(); Console.WriteLine($ "1w次调用结束,执行时间:{watch.ElapsedMilliseconds}ms" ); Console.ReadLine(); } while ( true ); }).Wait(); } |
测试100 object(注:测试参数传List 集合对象)
/// <summary> /// 测试 /// </summary> /// <param name="serviceProxyFactory"></param> public static void Test(IServiceProxyFactory serviceProxyFactory) { Task.Run(async () => { var userProxy = serviceProxyFactory.CreateProxy<IUserService>("User"); await userProxy.GetUserId("user"); var list = new List<UserModel>(); for(int i=0;i<100;i++) { list.Add(new UserModel { UserId = 1, Age = 18, Name = "fanly" }); } do { Console.WriteLine("正在循环 1w次调用 GetUser....."); //1w次调用 var watch = Stopwatch.StartNew(); for (var i = 0; i < 10000; i++) { var a =userProxy.Get(list).Result; } watch.Stop(); Console.WriteLine($"1w次调用结束,执行时间:{watch.ElapsedMilliseconds}ms"); Console.ReadLine(); } while (true); }).Wait(); }
通过以上测试代码,我们得到了如下的测试结果
通过上图,可以发现messagepack不管是小数据量还是大数据量都保持比较稳定的性能,而json.net 在100object平均已经达到了1.1ms,和messagepack、protobuffer比差太多,而 protobuffer在此次测试中表现的极其不稳定只有在1 object 和100 object 性能比较不错,但是与messagepack比还是相差比较大。所以我建议还是使用messagepack,性能上更优,侵入性也非常低
我们来看看性能最优的messagepack 详细测试数据
o object:
1 object:
10 object:
100 object
测试环境
CPU:Intel Core i7-4710MQ
内存:16G
硬盘:1T SSD+512G HDD
网络:局域网
6、总结
surging 已经完成JWT验证和AppSecret验证,下篇文章会详细介绍surging 身份认证,如感兴趣请多关注或者加入QQ群:615562965
【推荐】国内首个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,谁才是开发者新宠?