前言 小李:“胖子,上頭叫你對接我的數據好了沒有?” 胖子:“那是你的事,你都不提供數據源,我咋接?” 小李:“你想要什麼樣的數據源?” 胖子:“我想要一個調用簡單點的!” 小李:“我這個數據源是在linux平臺使用docker封裝發佈的,webapi的怎麼樣?” 胖子:“也行,這項目工期快用完了, ...
前言
小李:“胖子,上頭叫你對接我的數據好了沒有?”
胖子:“那是你的事,你都不提供數據源,我咋接?”
小李:“你想要什麼樣的數據源?”
胖子:“我想要一個調用簡單點的!”
小李:“我這個數據源是在linux平臺使用docker封裝發佈的,webapi的怎麼樣?”
胖子:“也行,這項目工期快用完了,你得提供api封裝sdk,另外我這邊對性能有要求的!”
小李:“webapi多好,基於json各個平臺都能對接,性能還不錯的!”
胖子:“我只關心我的業務,不是我的業務代碼,多一行我都不想碼,不然沒按時完成算你的!另外用webapi到時候請求量一大,到時候埠用完了,連接不了這鍋也得你背!”
小李:“我@##¥%*#¥@#&##@……”
面對胖子這些說辭,小李心裡面雖然一萬隻草泥馬在奔騰,但是項目還是要完成是不?另外胖子說的也不無道理!小李作為一個在C#下侵淫多年老鳥,很快想出一個辦法——rpc!首先當然是選wcf,這個巨硬的企業級產品在快速開發上除了配置上坑爹了一點,針對客戶端的對接真的非常快。小李仔細一研究wcf service 發現目前在linux下玩不了,心裡面又是了一陣@##¥%*#¥@#&##@……
胖子:“小李糾結啥,要不就弄個三方的搞一下算了,就算出事了,你說不定都已經離職了,怕啥……”
看著胖子一臉猥瑣的表情,小李那是一個氣啊,就怪自已平時牛逼吹上天,這時候怎麼好慫呢,一咬牙:“你放心,誤不了你的事!”。小李一邊回覆,心裡面開始盤算著自行實現一個功能簡易,性能高效,使用簡單的rpc了。
上面小李與胖子的場景,在開發的時候也是經典案例,回到正題來:本人認為rpc主要是:調用方法及參數序列化、socket傳輸、調用方法及參數反序列化、映射到本地並採用與請求相同流程回應客戶端的一套方案。其中關鍵點簡單分析主要有:序列化與反序列化、高性能tcp、遠程方法反轉、客戶端代碼生成四個方面;tcp還是使用iocp好了,其他接著一一分析。
序列化與反序列化
序列化與反序列化這個選二進位一般比json的好,ms版的BinaryFormatter 通用性強,但是他的性能、model的標記寫法等估計又要被噴了;找到Expression序列化,結果還是走的類似於soap xml這一套,想想算了:本地方法調用都是納秒級的,io都是毫秒級別的,socket的一次就傳這麼傳這麼大一堆,就算區域網也傷不起呀,想輕量化提升性能都難,自行實現一個簡單的好了。
1 /**************************************************************************** 2 *Copyright (c) 2018 Microsoft All Rights Reserved. 3 *CLR版本: 4.0.30319.42000 4 *機器名稱:WENLI-PC 5 *公司名稱:Microsoft 6 *命名空間:SAEA.RPC.Serialize 7 *文件名: SerializeUtil 8 *版本號: V1.0.0.0 9 *唯一標識:9e919430-465d-49a3-91be-b36ac682e283 10 *當前的用戶域:WENLI-PC 11 *創建人: yswenli 12 *電子郵箱:[email protected] 13 *創建時間:2018/5/22 13:17:36 14 *描述: 15 * 16 *===================================================================== 17 *修改標記 18 *修改時間:2018/5/22 13:17:36 19 *修改人: yswenli 20 *版本號: V1.0.0.0 21 *描述: 22 * 23 *****************************************************************************/ 24 using SAEA.RPC.Model; 25 using System; 26 using System.Collections.Generic; 27 using System.Text; 28 29 namespace SAEA.RPC.Serialize 30 { 31 /// <summary> 32 /// rpc參數序列化處理 33 /// </summary> 34 public class ParamsSerializeUtil 35 { 36 /// <summary> 37 /// len+data 38 /// </summary> 39 /// <param name="param"></param> 40 /// <returns></returns> 41 public static byte[] Serialize(object param) 42 { 43 List<byte> datas = new List<byte>(); 44 45 var len = 0; 46 byte[] data = null; 47 48 if (param == null) 49 { 50 len = 0; 51 } 52 else 53 { 54 if (param is string) 55 { 56 data = Encoding.UTF8.GetBytes((string)param); 57 } 58 else if (param is byte) 59 { 60 data = new byte[] { (byte)param }; 61 } 62 else if (param is bool) 63 { 64 data = BitConverter.GetBytes((bool)param); 65 } 66 else if (param is short) 67 { 68 data = BitConverter.GetBytes((short)param); 69 } 70 else if (param is int) 71 { 72 data = BitConverter.GetBytes((int)param); 73 } 74 else if (param is long) 75 { 76 data = BitConverter.GetBytes((long)param); 77 } 78 else if (param is float) 79 { 80 data = BitConverter.GetBytes((float)param); 81 } 82 else if (param is double) 83 { 84 data = BitConverter.GetBytes((double)param); 85 } 86 else if (param is DateTime) 87 { 88 var str = "wl" + ((DateTime)param).Ticks; 89 data = Encoding.UTF8.GetBytes(str); 90 } 91 else if (param is byte[]) 92 { 93 data = (byte[])param; 94 } 95 else 96 { 97 var type = param.GetType(); 98 99 if (type.IsGenericType || type.IsArray) 100 { 101 data = SerializeList((System.Collections.IEnumerable)param); 102 } 103 else if (type.IsGenericTypeDefinition) 104 { 105 data = SerializeDic((System.Collections.IDictionary)param); 106 } 107 else if (type.IsClass) 108 { 109 var ps = type.GetProperties(); 110 111 if (ps != null && ps.Length > 0) 112 { 113 List<object> clist = new List<object>(); 114 115 foreach (var p in ps) 116 { 117 clist.Add(p.GetValue(param)); 118 } 119 data = Serialize(clist.ToArray()); 120 } 121 } 122 } 123 len = data.Length; 124 } 125 datas.AddRange(BitConverter.GetBytes(len)); 126 if (len > 0) 127 { 128 datas.AddRange(data); 129 } 130 return datas.Count == 0 ? null : datas.ToArray(); 131 } 132 133 134 private static byte[] SerializeList(System.Collections.IEnumerable param) 135 { 136 List<byte> list = new List<byte>(); 137 138 if (param != null) 139 { 140 List<byte> slist = new List<byte>(); 141 142 foreach (var item in param) 143 { 144 var type = item.GetType(); 145 146 var ps = type.GetProperties(); 147 if (ps != null && ps.Length > 0) 148 { 149 List<object> clist = new List<object>(); 150 foreach (var p in ps) 151 { 152 clist.Add(p.GetValue(item)); 153 } 154 155 var clen = 0; 156 157 var cdata = Serialize(clist.ToArray()); 158 159 if (cdata != null) 160 { 161 clen = cdata.Length; 162 } 163 164 slist.AddRange(BitConverter.GetBytes(clen)); 165 slist.AddRange(cdata); 166 } 167 } 168 169 var len = 0; 170 171 if (slist.Count > 0) 172 { 173 len = slist.Count; 174 } 175 list.AddRange(BitConverter.GetBytes(len)); 176 list.AddRange(slist.ToArray()); 177 } 178 return list.ToArray(); 179 } 180 181 private static byte[] SerializeDic(System.Collections.IDictionary param) 182 { 183 List<byte> list = new List<byte>(); 184 185 if (param != null && param.Count > 0) 186 { 187 foreach (KeyValuePair item in param) 188 { 189 var type = item.GetType(); 190 var ps = type.GetProperties(); 191 if (ps != null && ps.Length > 0) 192 { 193 List<object> clist = new List<object>(); 194 foreach (var p in ps) 195 { 196 clist.Add(p.GetValue(item)); 197 } 198 var clen = 0; 199 200 var cdata = Serialize(clist.ToArray()); 201 202 if (cdata != null) 203 { 204 clen = cdata.Length; 205 } 206 207 list.AddRange(BitConverter.GetBytes(clen)); 208 list.AddRange(cdata); 209 } 210 } 211 } 212 return list.ToArray(); 213 } 214 215 /// <summary> 216 /// len+data 217 /// </summary> 218 /// <param name="params"></param> 219 /// <returns></returns> 220 public static byte[] Serialize(params object[] @params) 221 { 222 List<byte> datas = new List<byte>(); 223 224 if (@params != null) 225 { 226 foreach (var param in @params) 227 { 228 datas.AddRange(Serialize(param)); 229 } 230 } 231 232 return datas.Count == 0 ? null : datas.ToArray(); 233 } 234 235 /// <summary> 236 /// 反序列化 237 /// </summary> 238 /// <param name="types"></param> 239 /// <param name="datas"></param> 240 /// <returns></returns> 241 public static object[] Deserialize(Type[] types, byte[] datas) 242 { 243 List<object> list = new List<object>(); 244 245 var len = 0; 246 247 byte[] data = null; 248 249 int offset = 0; 250 251 for (int i = 0; i < types.Length; i++) 252 { 253 list.Add(Deserialize(types[i], datas, ref offset)); 254 } 255 256 return list.ToArray(); 257 } 258 259 /// <summary> 260 /// 反序列化 261 /// </summary> 262 /// <param name="type"></param> 263 /// <param name="datas"></param> 264 /// <param name="offset"></param> 265 /// <returns></returns> 266 public static object Deserialize(Type type, byte[] datas, ref int offset) 267 { 268 dynamic obj = null; 269 270 var len = 0; 271 272 byte[] data = null; 273 274 len = BitConverter.ToInt32(datas, offset); 275 offset += 4; 276 if (len > 0) 277 { 278 data = new byte[len]; 279 Buffer.BlockCopy(datas, offset, data, 0, len); 280 offset += len; 281 282 if (type == typeof(string)) 283 { 284 obj = Encoding.UTF8.GetString(data); 285 } 286 else if (type == typeof(byte)) 287 { 288 obj = (data); 289 } 290 else if (type == typeof(bool)) 291 { 292 obj = (BitConverter.ToBoolean(data, 0)); 293 } 294 else if (type == typeof(short)) 295 { 296 obj = (BitConverter.ToInt16(data, 0)); 297 } 298 else if (type == typeof(int)) 299 { 300 obj = (BitConverter.ToInt32(data, 0)); 301 } 302 else if (type == typeof(long)) 303 { 304 obj = (BitConverter.ToInt64(data, 0)); 305 } 306 else if (type == typeof(float)) 307 { 308 obj = (BitConverter.ToSingle(data, 0)); 309 } 310 else if (type == typeof(double)) 311 { 312 obj = (BitConverter.ToDouble(data, 0)); 313 } 314 else if (type == typeof(decimal)) 315 { 316 obj = (BitConverter.ToDouble(data, 0)); 317 } 318 else if (type == typeof(DateTime)) 319 { 320 var dstr = Encoding.UTF8.GetString(data); 321 var ticks = long.Parse(dstr.Substring(2)); 322 obj = (new DateTime(ticks)); 323 } 324 else if (type == typeof(byte[])) 325 { 326 obj = (byte[])data; 327 } 328 else if (type.IsGenericType) 329 { 330 obj = DeserializeList(type, data); 331 } 332 else if (type.IsArray) 333 { 334 obj = DeserializeArray(type, data); 335 } 336 else if (type.IsGenericTypeDefinition) 337 { 338 obj = DeserializeDic(type, data); 339 } 340 else if (type.IsClass) 341 { 342 var instance = Activator.CreateInstance(type); 343 344 var ts = new List<Type>(); 345 346 var ps = type.GetProperties(); 347 348 if (ps != null) 349 { 350 foreach (var p in ps) 351 { 352 ts.Add(p.PropertyType); 353 } 354 var vas = Deserialize(ts.ToArray(), data); 355 356 for (int j = 0; j < ps.Length; j++) 357 { 358 try 359 { 360 if (!ps[j].PropertyType.IsGenericType) 361 { 362 ps[j].SetValue(instance, Convert.ChangeType(vas[j], ps[j].PropertyType), null); 363 } 364 else 365 { 366 Type genericTypeDefinition = ps[j].PropertyType.GetGenericTypeDefinition(); 367 if (genericTypeDefinition == typeof(Nullable<>)) 368 { 369 ps[j].SetValue(instance, Convert.ChangeType(vas[j], Nullable.GetUnderlyingType(ps[j].PropertyType)), null); 370 } 371 else 372 { 373 //List<T>問題 374 ps[j].SetValue(instance, Convert.ChangeType(vas[j], ps[j].PropertyType), null); 375 } 376 } 377 } 378 catch (Exception ex) 379 { 380 Console.WriteLine("反序列化不支持的類型:" + ex.Message); 381 } 382 } 383 } 384 obj = (instance); 385 } 386 else 387 { 388 throw new RPCPamarsException("ParamsSerializeUtil.Deserialize 未定義的類型:" + type.ToString()); 389 } 390 391 } 392 return obj; 393 } 394 395 396 private static object DeserializeList(Type type, byte[] datas) 397 { 398 List<object> result = new List<object>(); 399 var stype = type.GenericTypeArguments[0]; 400 401 var len = 0; 402 var offset = 0; 403 //容器大小 404 len = BitConverter.ToInt32(datas, offset); 405 offset += 4; 406 byte[] cdata = new byte[len]; 407 Buffer.BlockCopy(datas, offset, cdata, 0, len); 408 offset += len; 409 410 //子項內容 411 var slen = 0; 412 var soffset = 0; 413 while (soffset < len) 414 { 415 slen = BitConverter.ToInt32(cdata, soffset); 416 var sdata = new byte[slen + 4]; 417 Buffer.BlockCopy(cdata, soffset, sdata, 0, slen + 4); 418 soffset += slen + 4; 419 420 if (slen > 0) 421 { 422 int lloffset = 0; 423 var sobj = Deserialize(stype, sdata, ref lloffset); 424 if (sobj != null) 425 result.Add(sobj); 426 } 427 else 428 { 429 result.Add(null); 430 } 431 } 432 return result; 433 } 434 435 private static object DeserializeArray(Type type, byte[] datas) 436 { 437 var obj = DeserializeList(type, datas); 438 439 if (obj == null) return null; 440 441 var list = (obj as List<object>); 442 443 return list.ToArray(); 444 } 445 446 private static object<