1. 程式人生 > >基於.NET CORE微服務框架 -談談surging 的messagepack、protobuffer、json.net 序列化

基於.NET CORE微服務框架 -談談surging 的messagepack、protobuffer、json.net 序列化

ces type posit rep factor bsp 技術分享 https 我們

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}。");
           }

客戶端:

   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(註:測試無參數)

  /// <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(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

技術分享圖片

6、總結

surging 已經完成JWT驗證和AppSecret驗證,下篇文章會詳細介紹surging 身份認證,如感興趣請多關註或者加入QQ群:615562965

基於.NET CORE微服務框架 -談談surging 的messagepack、protobuffer、json.net 序列化