服務與服務之間的調用


服務與服務之間的調用,

解決方案目前比較流行的有

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("服務調用異常失敗,請刷新再試!");
                }
            };
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM