lith 4 lat temu
rodzic
commit
45f51b4e06

+ 1 - 1
dotnet/Gateway/App.Gateway/appsettings.json

@@ -167,7 +167,7 @@
     },
 
     /* 队列模式,可不指定。可为 BlockingCollection(默认)、 ConsumerCache_BlockingCollection(高性能) 、ActionBlock、BufferBlock  */
-    "ConsumerMode": "ConsumerCache_BlockingCollection",
+    "ConsumerMode": "BlockingCollection",
 
 
     /* 序列化配置,可不指定 */

+ 3 - 5
dotnet/Library/Sers/Sers.Core/Sers.Core/Module/Rpc/RpcContextData.cs

@@ -16,8 +16,7 @@ namespace Sers.Core.Module.Rpc
 
         static RpcContextData()
         {
-            string rpcDataSerializeMode = ConfigurationManager.Instance.GetByPath<string>("Sers.RpcDataSerializeMode")
-                ?? "MessagePack";
+            string rpcDataSerializeMode = ConfigurationManager.Instance.GetByPath<string>("Sers.RpcDataSerializeMode");
 
             switch (rpcDataSerializeMode) 
             {
@@ -125,11 +124,10 @@ namespace Sers.Core.Module.Rpc
 
         public object error;
 
-        //public object user { get; set; }
 
-        object user_;
-        public object user { get => user_?.Serialize(); set => user_ = value; }
+        public object user;
 
+       
 
 
     }

+ 11 - 6
dotnet/Library/Vit/Vit.Core/Test/Vit.Core.Module.Serialization.Qps/Program.cs

@@ -1,4 +1,6 @@
-using Sers.Core.Module.Rpc;
+using Newtonsoft.Json.Linq;
+
+using Sers.Core.Module.Rpc;
 using Statistics;
 using System;
 using System.Threading;
@@ -8,7 +10,7 @@ namespace App
 {
     class Program
     {
-        public static int workThreadCount = 4;
+        public static int workThreadCount = 1;
 
 
         static void Main(string[] args)
@@ -66,12 +68,15 @@ namespace App
                         for (var t = 0; t < 10000; t++)
                         {
                             var data = new RpcContextData { route = "/a" };
-                            //data.http.method = "GET";
-                            //var str = Instance.SerializeToString(data);
+                            data.http.method = "GET";
 
-                            var bytes = Instance.SerializeToBytes(data);
+                            data.user = new JObject() { ["userInfo"] = "dd" };
+                            data.joUser = new JObject() { ["userInfo"] = "jo", ["age"] = 12, ["n"] = null, ["obj"] = new JArray { 1,true,12.5,DateTime.Now } };
 
-                            //var ss = bytes.BytesToString();
+                            var str = Instance.SerializeToString(data);
+
+                            var bytes = Instance.SerializeToBytes(data);
+ 
 
                             var data2 = Instance.DeserializeFromBytes<RpcContextData>(bytes);
                         }

+ 4 - 1
dotnet/Library/Vit/Vit.Core/Test/Vit.Core.Module.Serialization.Qps/RpcContextData.cs

@@ -2,6 +2,8 @@
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 
+using Newtonsoft.Json.Linq;
+
 namespace Sers.Core.Module.Rpc
 {
     public class RpcContextData
@@ -49,7 +51,8 @@ namespace Sers.Core.Module.Rpc
 
         public object user;
 
- 
+        public JObject joUser;
+
 
     }
 }

+ 341 - 12
dotnet/Library/Vit/Vit.Core/Vit.Core/Module/Serialization/Serialization_MessagePack.cs

@@ -1,6 +1,10 @@
 using System;
 using System.Runtime.CompilerServices;
 using MessagePack;
+using MessagePack.Formatters;
+using MessagePack.Resolvers;
+using Newtonsoft.Json.Linq;
+using Vit.Extensions;
 
 namespace Vit.Core.Module.Serialization
 {
@@ -14,6 +18,32 @@ namespace Vit.Core.Module.Serialization
 
 
 
+        protected MessagePackSerializerOptions options;
+        public Serialization_MessagePack()
+        {
+ 
+            var resolver = MessagePack.Resolvers.CompositeResolver.Create(
+                new IMessagePackFormatter[]
+                {  
+
+                    NewtonsoftFormatter_JObject.Instance,
+                    NewtonsoftFormatter_JArray.Instance,
+
+                    //NilFormatter.Instance,
+                    //new IgnoreFormatter<MethodBase>(),
+                
+                 },
+                new IFormatterResolver[]
+                {                 
+                     //ContractlessStandardResolver.Options.Resolver,
+                     ContractlessStandardResolver.Instance
+                });
+
+            options = MessagePack.Resolvers.ContractlessStandardResolver.Options.WithResolver(resolver);
+        }
+
+
+
         #region (x.1)object <--> String
 
         #region SerializeToString
@@ -21,7 +51,7 @@ namespace Vit.Core.Module.Serialization
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public string SerializeToString<T>(T value)
         {
-            return MessagePackSerializer.ConvertToJson(SerializeToBytes(value));
+            return MessagePackSerializer.ConvertToJson(SerializeToBytes(value), options);
         }
 
 
@@ -29,7 +59,7 @@ namespace Vit.Core.Module.Serialization
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public string SerializeToString(object value,Type type)
         {
-            return MessagePackSerializer.ConvertToJson(SerializeToBytes(value));
+            return MessagePackSerializer.ConvertToJson(SerializeToBytes(value), options);
         }
 
         #endregion
@@ -39,13 +69,18 @@ namespace Vit.Core.Module.Serialization
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public T DeserializeFromString<T>(string value)
         {
-            throw new NotImplementedException(); 
+            //throw new NotImplementedException();
+            var bytes = MessagePackSerializer.ConvertFromJson(value, options);
+            return DeserializeFromBytes<T>(bytes);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public object DeserializeFromString(string value, Type type)
         {
-            throw new NotImplementedException();
+            //throw new NotImplementedException();
+
+            var bytes = MessagePackSerializer.ConvertFromJson(value, options);
+            return DeserializeFromBytes(bytes, type);       
         }
 
         #endregion
@@ -62,8 +97,7 @@ namespace Vit.Core.Module.Serialization
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public byte[] SerializeToBytes<T>(T value)
         {
-            return MessagePackSerializer.Serialize<T>(value,
-             MessagePack.Resolvers.ContractlessStandardResolver.Options);          
+            return MessagePackSerializer.Serialize<T>(value, options);          
         }
 
 
@@ -71,8 +105,7 @@ namespace Vit.Core.Module.Serialization
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public byte[] SerializeToBytes(object value,Type type)
         {
-            return MessagePackSerializer.Serialize(type,value,
-             MessagePack.Resolvers.ContractlessStandardResolver.Options);
+            return MessagePackSerializer.Serialize(type,value, options);
         }
         #endregion
 
@@ -102,15 +135,13 @@ namespace Vit.Core.Module.Serialization
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public T DeserializeFromSpan<T>(ReadOnlyMemory<byte> bytes)
         {        
-            return MessagePackSerializer.Deserialize<T>(bytes,
-             MessagePack.Resolvers.ContractlessStandardResolver.Options);
+            return MessagePackSerializer.Deserialize<T>(bytes, options);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public object DeserializeFromSpan(ReadOnlyMemory<byte> bytes, Type type)
         {    
-            return MessagePackSerializer.Deserialize(type, bytes,
-             MessagePack.Resolvers.ContractlessStandardResolver.Options);
+            return MessagePackSerializer.Deserialize(type, bytes, options);
         }
         #endregion
 
@@ -130,5 +161,303 @@ namespace Vit.Core.Module.Serialization
         }
         #endregion
 
+
+
+
+
+
+
+        #region NewtonsoftFormatter_SaveAsString
+        private class NewtonsoftFormatter_SaveAsString<NewtonsoftType> : IMessagePackFormatter<NewtonsoftType>
+            //where NewtonsoftType: Newtonsoft.Json.Linq.JToken
+        {
+            public void Serialize(
+              ref MessagePackWriter writer, NewtonsoftType value, MessagePackSerializerOptions options)
+            {
+                if (value == null)
+                {
+                    writer.WriteNil();
+                    return;
+                }
+
+                var str = Serialization_Newtonsoft.Instance.SerializeToString(value);
+                writer.Write(str);
+            }
+
+            public NewtonsoftType Deserialize(
+              ref MessagePackReader reader, MessagePackSerializerOptions options)
+            {
+                if (reader.TryReadNil())
+                {
+                    return default;
+                }
+
+                options.Security.DepthStep(ref reader);
+
+                var data = reader.ReadString();
+
+                reader.Depth--;
+
+                return Newtonsoft.Json.JsonConvert.DeserializeObject<NewtonsoftType>(data);        
+            }
+        }
+
+
+        #endregion
+
+
+        #region NewtonsoftFormatter JObject JArray 
+
+
+        private class NewtonsoftFormatter_JObject  : IMessagePackFormatter<Newtonsoft.Json.Linq.JObject>         
+        {
+
+            public static readonly NewtonsoftFormatter_JObject Instance = new NewtonsoftFormatter_JObject();
+
+            public void Serialize(
+              ref MessagePackWriter writer, Newtonsoft.Json.Linq.JObject value, MessagePackSerializerOptions options)
+            {
+                if (value == null)
+                {
+                    writer.WriteNil();
+                    return;
+                }
+
+
+                writer.WriteMapHeader(value.Count);
+             
+
+                //IMessagePackFormatter<JObject> objectFormatter = null;
+                //IMessagePackFormatter<JArray> arrayFormatter = null;
+
+                foreach (var kv in value) 
+                {
+                    //key
+                    writer.Write(kv.Key);
+             
+
+                    //value
+                    if (kv.Value.IsNull()) 
+                    {
+                        writer.WriteNil();
+                        continue;
+                    }
+
+                    switch (kv.Value)
+                    {                        
+                        case JObject jo:
+                            //if (objectFormatter == null) objectFormatter = options.Resolver.GetFormatterWithVerify<JObject>();
+                            //objectFormatter?.Serialize(ref writer, jo, options); 
+                            Serialize(ref writer, jo, options);
+                            break;
+                        case JArray ja:
+                            //if (arrayFormatter == null) arrayFormatter = options.Resolver.GetFormatterWithVerify<JArray>();
+                            //arrayFormatter?.Serialize(ref writer, ja, options);
+                            NewtonsoftFormatter_JArray.Instance.Serialize(ref writer, ja, options);
+                            break;
+                     
+                        case JValue jv:
+                            switch (jv.Type) 
+                            {
+                                case JTokenType.String: writer.Write(jv.Value<string>()); break;
+                                case JTokenType.Integer: writer.Write(jv.Value<long>()); break;
+                                case JTokenType.Float: writer.Write(jv.Value<double>()); break;
+                                case JTokenType.Boolean: writer.Write(jv.Value<bool>()); break;
+                                case JTokenType.Date: writer.Write(jv.Value<DateTime>().ToString("yyyy-MM-dd HH:mm:ss")); break;
+                                default: writer.Write(Serialization_Newtonsoft.Instance.SerializeToString(jv)); break;
+                            }
+                            break;
+                        default:
+                            string str = Serialization_Newtonsoft.Instance.SerializeToString(kv.Value);
+                            writer.Write(str); 
+                            break;
+                    }
+                } 
+            }
+
+            public Newtonsoft.Json.Linq.JObject Deserialize(
+              ref MessagePackReader reader, MessagePackSerializerOptions options)
+            {
+                if (reader.TryReadNil())
+                {
+                    return default;
+                }
+
+                var result = new JObject();
+                int count = reader.ReadMapHeader();
+                if (count == 0) return result;
+
+                options.Security.DepthStep(ref reader);
+                try
+                {
+                    //IMessagePackFormatter<JObject> objectFormatter = null;
+                    //IMessagePackFormatter<JArray> arrayFormatter = null;
+
+                    for (int i = 0; i < count; i++)
+                    {
+                        string key = reader.ReadString();
+
+                        switch (reader.NextMessagePackType)
+                        {
+                            case MessagePackType.Map:
+                                //if (objectFormatter == null) objectFormatter = options.Resolver.GetFormatterWithVerify<JObject>();
+                                //result[key] = objectFormatter?.Deserialize(ref reader, options);
+                                result[key] = Deserialize(ref reader, options);
+
+                                break;
+                            case MessagePackType.Array:
+                                //if (arrayFormatter == null) arrayFormatter = options.Resolver.GetFormatterWithVerify<JArray>();
+                                //result[key] = arrayFormatter?.Deserialize(ref reader, options);
+                                result[key] = NewtonsoftFormatter_JArray.Instance.Deserialize(ref reader, options);
+                                break;
+                            case MessagePackType.Integer: result[key] = reader.ReadInt64(); break;
+                            case MessagePackType.Boolean: result[key] = reader.ReadBoolean(); break;
+                            case MessagePackType.Float: result[key] = reader.ReadDouble(); break;
+                            case MessagePackType.String: result[key] = reader.ReadString(); break;
+                            case MessagePackType.Nil:
+                                result[key] = null;
+                                reader.Skip();
+                                break;
+                            default:
+                                result[key] = null;
+                                reader.Skip();
+                                break;
+                        }
+
+                    }
+                }
+                finally
+                {
+                    reader.Depth--;
+                }
+
+
+                return result;
+            }
+        }
+
+        private class NewtonsoftFormatter_JArray : IMessagePackFormatter<Newtonsoft.Json.Linq.JArray>
+        {
+            public static readonly NewtonsoftFormatter_JArray Instance = new NewtonsoftFormatter_JArray();
+
+
+            public void Serialize(
+              ref MessagePackWriter writer, Newtonsoft.Json.Linq.JArray value, MessagePackSerializerOptions options)
+            {
+                if (value == null)
+                {
+                    writer.WriteNil();
+                    return;
+                }
+
+
+                writer.WriteArrayHeader(value.Count);
+
+
+                //IMessagePackFormatter<JObject> objectFormatter = null;
+                //IMessagePackFormatter<JArray> arrayFormatter = null;
+
+                foreach (var token in value)
+                {
+                    if (token.IsNull()) 
+                    {
+                        writer.WriteNil();
+                        continue;
+                    }
+                    switch (token)
+                    {
+                        case JObject jo:
+                            //if (objectFormatter == null) objectFormatter = options.Resolver.GetFormatterWithVerify<JObject>();
+                            //objectFormatter?.Serialize(ref writer, jo, options);
+                            NewtonsoftFormatter_JObject.Instance.Serialize(ref writer, jo, options);
+                            break;
+                        case JArray ja:
+                            //if (arrayFormatter == null) arrayFormatter = options.Resolver.GetFormatterWithVerify<JArray>();
+                            //arrayFormatter?.Serialize(ref writer, ja, options);
+                            Serialize(ref writer, ja, options);
+                            break;
+                        case JValue jv:
+                            switch (jv.Type)
+                            {
+                                case JTokenType.String: writer.Write(jv.Value<string>()); break;
+                                case JTokenType.Integer: writer.Write(jv.Value<long>()); break;
+                                case JTokenType.Float: writer.Write(jv.Value<double>()); break;
+                                case JTokenType.Boolean: writer.Write(jv.Value<bool>()); break;
+                                case JTokenType.Date: writer.Write(jv.Value<DateTime>().ToString("yyyy-MM-dd HH:mm:ss")); break;
+                                default: writer.Write(Serialization_Newtonsoft.Instance.SerializeToString(jv)); break;
+                            }
+                           break;
+                        default:
+                            string str = Serialization_Newtonsoft.Instance.SerializeToString(token);
+                            writer.Write(str);
+                            break;
+                    }
+
+                }
+            }
+
+            public Newtonsoft.Json.Linq.JArray Deserialize(
+              ref MessagePackReader reader, MessagePackSerializerOptions options)
+            {
+                if (reader.TryReadNil())
+                {
+                    return default;
+                }
+
+                var result = new JArray();
+                int count = reader.ReadArrayHeader();
+                if (count == 0) return result;
+
+                options.Security.DepthStep(ref reader);
+                try
+                {
+                    //IMessagePackFormatter<JObject> objectFormatter = null;
+                    //IMessagePackFormatter<JArray> arrayFormatter = null;
+
+                    for (int i = 0; i < count; i++)
+                    {                      
+
+                        switch (reader.NextMessagePackType)
+                        {
+                            case MessagePackType.Map:
+                                //if (objectFormatter == null) objectFormatter = options.Resolver.GetFormatterWithVerify<JObject>();
+                                //result.Add( objectFormatter?.Deserialize(ref reader, options));
+                                result.Add(NewtonsoftFormatter_JObject.Instance.Deserialize(ref reader, options));
+                                break;
+                            case MessagePackType.Array:
+                                //if (arrayFormatter == null) arrayFormatter = options.Resolver.GetFormatterWithVerify<JArray>();
+                                //result.Add(arrayFormatter?.Deserialize(ref reader, options));
+                                result.Add(Deserialize(ref reader, options));
+                                break;
+                            case MessagePackType.Integer: result.Add(reader.ReadInt64()); break;
+                            case MessagePackType.Boolean: result.Add(reader.ReadBoolean()); break;
+                            case MessagePackType.Float: result.Add(reader.ReadDouble()); break;
+                            case MessagePackType.String: result.Add(reader.ReadString()); break;
+                            case MessagePackType.Nil:
+                                result.Add(null);
+                                reader.Skip();
+                                break;
+                            default:
+                                result.Add(null);
+                                reader.Skip();
+                                break;
+                        }
+
+                    }
+                }
+                finally
+                {
+                    reader.Depth--;
+                }
+
+
+                return result;
+            }
+        }
+
+        #endregion
+
+ 
+
     }
 }

+ 31 - 7
dotnet/Library/Vit/Vit.Core/Vit.Core/Module/Serialization/Serialization_Newtonsoft.cs

@@ -169,7 +169,8 @@ namespace Vit.Core.Module.Serialization
 
             if (type.TypeIsValueTypeOrStringType())
             {
-                return value.Convert<string>();
+                //return value.Convert<string>();
+                return value.ToString();
             }
             return JsonConvert.SerializeObject(value, serializeSetting);
         }
@@ -181,13 +182,24 @@ namespace Vit.Core.Module.Serialization
         /// <summary>
         /// 使用Newtonsoft反序列化。T也可为值类型(例如 int?、bool) 
         /// </summary>
-        /// <param name="value"></param>
-        /// <param name="type"></param>
+        /// <param name="value"></param>    
         /// <returns></returns>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public T DeserializeFromString<T>(string value)
         {
-            return (T)DeserializeFromString(value,typeof(T));
+            //return (T)DeserializeFromString(value,typeof(T));
+            if (null == value) return default;
+
+            Type type = typeof(T);
+
+            if (type.TypeIsValueTypeOrStringType())
+            {
+                return (T)DeserializeStruct(value, type);
+            }
+
+            //if (string.IsNullOrWhiteSpace(value)) return type.DefaultValue();
+
+            return JsonConvert.DeserializeObject<T>(value);
         }
 
 
@@ -207,7 +219,7 @@ namespace Vit.Core.Module.Serialization
                 return DeserializeStruct(value, type);
             }
 
-            if (string.IsNullOrWhiteSpace(value)) return type.DefaultValue();
+            //if (string.IsNullOrWhiteSpace(value)) return type.DefaultValue();
 
             return JsonConvert.DeserializeObject(value, type);
         }
@@ -225,15 +237,27 @@ namespace Vit.Core.Module.Serialization
         #region (x.2)object <--> bytes
 
         #region SerializeToBytes
+        /// <summary>
+        /// T 可以为   byte[]、string、 object 、struct
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public byte[] SerializeToBytes<T>(T obj) 
+        {
+            return SerializeToBytes<T>(obj,null);
+        }
 
         /// <summary>
         /// T 可以为   byte[]、string、 object 、struct
         /// </summary>
         /// <typeparam name="T"></typeparam>
         /// <param name="obj"></param>
+        /// <param name="encoding"></param>
         /// <returns></returns>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public byte[] SerializeToBytes<T>(T obj)
+        public byte[] SerializeToBytes<T>(T obj, Encoding encoding)
         {
             if (null == obj) return null;
 
@@ -249,7 +273,7 @@ namespace Vit.Core.Module.Serialization
                 default: strValue = SerializeToString(obj); break;
             }
 
-            return StringToBytes(strValue);
+            return StringToBytes(strValue, encoding);
         }
 
 

+ 2 - 2
dotnet/ServiceCenter/App.Gover.Gateway/appsettings.json

@@ -32,7 +32,7 @@
 
 
     /* RpcData序列化模式,可不指定。可为 Text(默认)、Newtonsoft、MessagePack。MessagePack效率最快,但传递方式不为json字符串。 */
-    "RpcDataSerializeMode": "MessagePack",
+    "RpcDataSerializeMode": "Text",
 
 
     /* ServiceStation配置,可不指定 */
@@ -167,7 +167,7 @@
     },
 
     /* 队列模式,可不指定。可为 BlockingCollection(默认)、 ConsumerCache_BlockingCollection(高性能) 、ActionBlock、BufferBlock  */
-    "ConsumerMode": "ConsumerCache_BlockingCollection",
+    "ConsumerMode": "BlockingCollection",
 
 
     /* 序列化配置,可不指定 */

+ 2 - 2
dotnet/ServiceCenter/App.ServiceCenter/appsettings.json

@@ -166,7 +166,7 @@
 
 
     /* RpcData序列化模式,可不指定。可为 Text(默认)、Newtonsoft、MessagePack。MessagePack效率最快,但传递方式不为json字符串。 */
-    "RpcDataSerializeMode": "MessagePack",
+    "RpcDataSerializeMode": "Text",
 
 
 
@@ -522,7 +522,7 @@
     },
 
     /* 队列模式,可不指定。可为 BlockingCollection(默认)、 ConsumerCache_BlockingCollection(高性能) 、ActionBlock、BufferBlock  */
-    "ConsumerMode": "ConsumerCache_BlockingCollection",
+    "ConsumerMode": "BlockingCollection",
 
     /* 序列化配置,可不指定 */
     "Serialization": {

+ 2 - 2
dotnet/ServiceStation/Demo/SersLoader/Did.SersLoader.Demo/appsettings.json

@@ -153,7 +153,7 @@
 
 
     /* RpcData序列化模式,可不指定。可为 Text(默认)、Newtonsoft、MessagePack。MessagePack效率最快,但传递方式不为json字符串。 */
-    "RpcDataSerializeMode": "MessagePack",
+    "RpcDataSerializeMode": "Text",
 
 
     /* LocalApiService 配置,可不指定 */
@@ -342,7 +342,7 @@
     },
 
     /* 队列模式,可不指定。可为 BlockingCollection(默认)、 ConsumerCache_BlockingCollection(高性能) 、ActionBlock、BufferBlock  */
-    "ConsumerMode": "ConsumerCache_BlockingCollection",
+    "ConsumerMode": "BlockingCollection",
 
 
     /* 序列化配置,可不指定 */

+ 2 - 2
dotnet/ServiceStation/Demo/Serslot/Did.Serslot.Demo/appsettings.json

@@ -40,7 +40,7 @@
 
 
     /* RpcData序列化模式,可不指定。可为 Text(默认)、Newtonsoft、MessagePack。MessagePack效率最快,但传递方式不为json字符串。 */
-    "RpcDataSerializeMode": "MessagePack",
+    "RpcDataSerializeMode": "Text",
 
 
     /* LocalApiService 配置,可不指定 */
@@ -201,7 +201,7 @@
     },
 
     /* 队列模式,可不指定。可为 BlockingCollection(默认)、 ConsumerCache_BlockingCollection(高性能) 、ActionBlock、BufferBlock  */
-    "ConsumerMode": "ConsumerCache_BlockingCollection",
+    "ConsumerMode": "BlockingCollection",
 
 
     /* 序列化配置,可不指定 */

+ 5 - 0
readme.md

@@ -43,6 +43,11 @@ win10(vr)      ApiClientAsync 4/8	      32万(50%)
 win10(vr)      ApiClientAsync 4/4	      33万(50%)
 
 
+4/8   55万   52%
+4/16  53万   53%
+8/8   65万   88%
+8/16  63万   86%
+
 
 i7 7700K 4核8线程  ApiClientAsync 
 win10(4.2GHZ)   16/32	      33万(90%)