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
序列化)
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}。');
}
客户端:
/// <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();
}
测试 0 object(注:测试无参数)
/// <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(注:测试参数传对象)
/// <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 集合对象)
/// <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(, Age = , 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
网络:局域网