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

fanly11發表於2017-11-18

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

測試環境

CPU:Intel Core i7-4710MQ

記憶體:16G

硬碟:1T SSD+512G HDD

網路:區域網

6、總結

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

   

相關文章