服務與服務之間的呼叫,
解決方案目前比較流行的有
1:基於rpc,如微軟推薦的:grpc,建立grpc服務後可以生成proco等檔案
1:什麼是GRPC? gRPC是與語言無關的高效能遠端過程呼叫(RPC)框架。谷歌開發的grpc技術框架,C#端由微軟的員工來維護升級 2: 使用GRPC有啥好處? 合同優先的API開發,預設情況下使用協議緩衝區,允許使用與語言無關的實現。 可用於多種語言的工具,以生成強型別的伺服器和客戶端。 支援客戶端,伺服器和雙向流呼叫。 通過Protobuf二進位制序列化減少網路使用。 GRPC的主要優點是: GRPC是與語言無關的高效能遠端過程呼叫(RPC)框架。 現代,高效能,輕量級的RPC框架。 效率至關重要的輕量級微服務。 開發需要多種語言的多語言系統。 需要處理流式請求或響應的點對點實時服務。 3:啥時候使用和不建議使用GRPC? 直播,聊天等不適合 4:使用GRPC的步驟
***************下次整理寫一個案例,目前網路上有很多可以參考,長時間不使用容易忘記**************
這次決定還是不使用GRPC,雖然效率上高一些,還得再多搞一個服務和通用的配置出來,而且如果服務多配置檔案也會比較多,
配置上也感覺不是很喜歡,所以就使用比較傳統的方案二
2:網路請求的工具,如系統自帶的 IHttpClientFactory(從.NetCore 2.1官方已經優化),不是HttpClient(缺點可以網路上查詢)
get請求
get測試效果截圖:
post請求
post測試效果截圖:
呼叫的步驟為:服務07呼叫06的服務,本地測試為ip一致,但是埠不一致,模擬不同的服務之間的呼叫
為啥要這樣設計?
1:首先將各個服務的host如:http://localhost:8081等等都寫在appsetting配置檔案中, 這樣方便維護和修改,一處修改處處修改的效果,修改替換後也不需要構建映象再發布,直接Docker重啟即可。 2:獲取配置資訊:採用列舉+常量+靜態類,清晰明瞭,使用時不用到處宣告變數,避免多次申請記憶體空間,提升系統執行效率。 3:基於第二點,有列舉,選擇呼叫哪一個服務時方便高效點出來, HttpFactoryClient網路請求為優化後的解決方案,不用擔心之前的版本Tcp/ip資源不能及時釋放,而導致無資源可呼叫,因為已經內建pool池化。
code主要程式碼如下:
using GDBS.Shared.Data; using GDBS.Shared.Data.Const; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace GDBS.Shared.ToolKits.Tool { /// <summary> /// 跨服務 WebApi部分幫助類 jason 待進一步完善 /// </summary> public class HttpClientHelper { private static string GetHostStrByConst(PlatFormEnum optionEnum) { string gethost = string.Empty; switch (optionEnum) { case PlatFormEnum.BridgeService: gethost = PlatFormApiHostOptions.BridgeService; break; case PlatFormEnum.GdbsProjectService: gethost = PlatFormApiHostOptions.GdbsProjectService; break; case PlatFormEnum.ReceAndSendMsgService: gethost = PlatFormApiHostOptions.ReceAndSendMsgService; break; case PlatFormEnum.ProvincialLevelService: gethost = PlatFormApiHostOptions.ProvincialLevelService; break; case PlatFormEnum.ThirdPartyService: gethost = PlatFormApiHostOptions.ThirdPartyService; break; case PlatFormEnum.MonitoringService: gethost = PlatFormApiHostOptions.MonitoringService; break; case PlatFormEnum.LogService: gethost = PlatFormApiHostOptions.LogService; break; default: gethost = PlatFormApiHostOptions.BridgeService; break; } return gethost; } /// <summary> /// 服務之間的網路請求,PostAsync非同步處理 /// </summary> /// <typeparam name="T">T 通常返回的實體資料</typeparam> /// <param name="_httpContext">請求上下文</param> /// <param name="_httpClientFactory">網路請求物件</param> /// <param name="hostEnum">服務列舉的一個域名,如:http://localhost:8081</param> /// <param name="requestApiUrlPath">服務的具體請求地址,開頭為:/api/....如:/api/ReceAndSendMsgService/abc/testapi</param> /// <param name="dataDto">有資料的話約定為一個實體物件 如:var datatDto = new { Id = 1001, Name = "LMZ",Age=18 }</param> /// <param name="timeOut">預設連線超時時間為50秒</param> /// <paramref name="returnModel">預設true:返回實體資料;false:返回字串</paramref> /// <returns></returns> public static async Task<T> PostAsync<T>(IHttpClientFactory _httpClientFactory, HttpContext _httpContext, string requestApiUrlPath, PlatFormEnum hostEnum = PlatFormEnum.BridgeService, object dataDto = null, int timeOut = 50) { if (string.IsNullOrEmpty(requestApiUrlPath)) throw new Exception("請求服務的具體介面地址不可以為空!"); if (!requestApiUrlPath.Contains("/api")) throw new Exception("介面地址錯誤,應該為如:/api/AbcService/****"); string content = dataDto != null ? JsonConvert.SerializeObject(dataDto) : "0"; var jsonDataStr = new StringContent(content, Encoding.UTF8, WebApiHeaderStrConst.Head_MediaType); var authVal = _httpContext.Request.Headers[WebApiHeaderStrConst.Head_Authorization].ToString().Replace(WebApiHeaderStrConst.Head_Authorization, ""); using var client = _httpClientFactory.CreateClient(); client.DefaultRequestHeaders.Add(WebApiHeaderStrConst.Head_Authorization, authVal); var connectTimeOut = timeOut <= 0 || timeOut > 180 ? 50 : timeOut; client.Timeout = TimeSpan.FromSeconds(connectTimeOut); var host = GetHostStrByConst(hostEnum); //string requesturl = $"{host}{requestApiUrlPath}"; using (var httpResponseMsg = await client.PostAsync($"{host}{requestApiUrlPath}", jsonDataStr)) { if (httpResponseMsg.IsSuccessStatusCode) { var getstr = await httpResponseMsg.Content.ReadAsStringAsync(); Console.WriteLine("getStr=" + getstr); return JsonConvert.DeserializeObject<T>(getstr); } else { throw new Exception("服務呼叫異常失敗,請重新整理再試!"); } }; } /// <summary> /// 服務之間的網路請求,GetAsync非同步處理 /// </summary> /// <param name="_httpContext">請求上下文</param> /// <param name="_httpClientFactory">網路請求物件</param> /// <param name="hostEnum">服務列舉的一個域名,如:http://localhost:8081</param> /// <param name="RequestApiUrlPath">服務的具體請求地址,開頭為:/api/.... 如:/api/ReceAndSendMsgService/abc/testapi</param> /// <param name="RequestDataDic">傳輸的資料字典 Dictionary<string, object> </param> /// <param name="timeOut">預設連線超時時間為50秒</param> /// <paramref name="returnModel">預設true:返回實體資料;false:返回字串</paramref> /// <returns></returns> public static async Task<T> GetAsync<T>(IHttpClientFactory _httpClientFactory, HttpContext _httpContext, string RequestApiUrlPath, PlatFormEnum hostEnum = PlatFormEnum.BridgeService, Dictionary<string, object> RequestDataDic = null, int timeOut = 50, bool returnModel = true) { if (string.IsNullOrEmpty(RequestApiUrlPath)) throw new Exception("請求服務的具體介面地址不可以為空!"); if (!RequestApiUrlPath.Contains("/api")) throw new Exception("介面地址錯誤,應該為如:/api/AbcService/****"); using var client = _httpClientFactory.CreateClient(); var authVal = _httpContext.Request.Headers[WebApiHeaderStrConst.Head_Authorization].ToString().Replace(WebApiHeaderStrConst.Head_Authorization, ""); client.DefaultRequestHeaders.Add(WebApiHeaderStrConst.Head_Authorization, authVal); var connectTimeOut = timeOut <= 0 || timeOut > 180 ? 50 : timeOut; client.Timeout = TimeSpan.FromSeconds(connectTimeOut); var sb = new StringBuilder(); if (RequestDataDic != null) { foreach (var dickey in RequestDataDic.Keys) sb.Append($"&{dickey}={RequestDataDic[dickey]}"); } var host = GetHostStrByConst(hostEnum); string requestData = $"{host}{RequestApiUrlPath}?lmz_temp=0{sb}"; using (var httpResponseMsg = await client.GetAsync(requestData)) { if (httpResponseMsg.IsSuccessStatusCode) { var getstr = await httpResponseMsg.Content.ReadAsStringAsync(); Console.WriteLine("getStr=" + getstr + ", sbdata=" + sb); return JsonConvert.DeserializeObject<T>(getstr); } else { throw new Exception("服務呼叫異常失敗,請重新整理再試!"); } }; } } }