.netcore 3.1高性能微服務架構:封裝調用外部服務的介面方法--HttpClient客戶端思路分析

来源:https://www.cnblogs.com/puzi0315/archive/2020/01/29/12239442.html

眾所周知,微服務架構是由一眾微服務組成,項目中調用其他微服務介面更是常見的操作。為了便於調用外部介面,我們的常用思路一般都是封裝一個外部介面的客戶端,使用時候直接調用相應的方法。webservice或WCF的做法就是引用服務,自動生成客戶端。在webapi2.0里,我們都會手動封裝一個靜態類。那麼在 ...


眾所周知,微服務架構是由一眾微服務組成,項目中調用其他微服務介面更是常見的操作。為了便於調用外部介面,我們的常用思路一般都是封裝一個外部介面的客戶端,使用時候直接調用相應的方法。webservice或WCF的做法就是引用服務,自動生成客戶端。在webapi2.0里,我們都會手動封裝一個靜態類。那麼在.netcore3.1的微服務時代,我們該如何處理這個問題呢? ----思路都是一樣的,封裝一個外部服務,並且使用依賴註入和 HttpFactory工廠等.netcore特有的方式提升性能。接下來我們一步一步說下詳細的步驟:

第1步:--創建項目

為了便於構建生成nuget包,我們一般都每個外部服務創建一個獨立的項目;如下圖:

在解決方案里,我們創建了一個項目名為:"JR.UPC.Template.OuterClient",(項目起名一般為 公司名.部門名.項目名.xxx)。

第2步:創建一個IServiceCollection擴展服務,便於將服務註冊信息。(重點)

 

    public static  class MsgApiClientServiceCollectionExtensions
    {
        public static IServiceCollection AddMsgApiClient(this IServiceCollection services, IConfiguration MsgClientConfiguration)
        {
            services.Configure<MsgClientConfiguration>(MsgClientConfiguration)
                    .AddHttpClient<IMsgApiClient, MsgApiClient>()
                    .ConfigureHttpClient(config=> {
                        config.BaseAddress = new Uri(MsgClientConfiguration.GetSection("url").Value);
                        config.Timeout = TimeSpan.FromSeconds(30);
                        });

            return services;
        }
    }

該段代碼雖然很短,但是最關鍵的部分:

代碼的執行過程如下:

(1)  services首先註冊一個操作配置文件的實例 :

services.Configure<MsgClientConfiguration>(MsgClientConfiguration)

 

(2) 添加HttpClientFactory工廠並且關聯到services里,並將Client強制類型為IMsgApiClient(自定義的外部微服務介面名稱) :

.AddHttpClient<IMsgApiClient, MsgApiClient>();//IMsgApiClient為接下來要創建的客戶端

 

(3)設置HttpClient的相關配置參數:

 .ConfigureHttpClient(config=> {
                        config.BaseAddress = new Uri(MsgClientConfiguration.GetSection("url").Value);//外部微服務介面功能變數名稱
                        config.Timeout = TimeSpan.FromSeconds(30);  // 介面調用超時時間
                        });

 

還有如下註意點:

(1)以靜態類和靜態方法方式;

  (2) this IServiceCollection services ,這個參數我就不多解釋了。

 (3) 一般我們都需要讀取appsettings.json配置文件里的參數所以這裡接收了一個參數---IConfiguration MsgClientConfiguration  ;當然如果你不需要讀取配置參數,也可以忽略這個參數;

 

第3步:寫外部介面調用的具體邏輯代碼:

(1)創建一個介面,比如IMsgApiClient

 /// <summary>
    /// 企業微信消息發送客戶端
    /// </summary>
    public interface IMsgApiClient
    {

        /// <summary>
        /// 調用介面介面:向微信發送消息
        /// </summary>
        /// <param name="hrcodeStr">hrcode,多個以|隔開</param>
        /// <param name="msg">消息內容</param>
        /// <returns></returns>
        Task<Result<string>> SendWxWorkMsgAsync(string hrcodeStr, string msg);

    }

(2)實現該介面:

  /// <summary>
        /// 調用外部介面:向SD發送消息
        /// </summary>
        /// <param name="hrcodeStr">hrcode,多個以|隔開</param>
        /// <param name="msg">消息內容</param>
        /// <returns></returns>
        public async Task<Result<string>> SendWxWorkMsgAsync(string hrcodeStr, string msg)
        {
            Result<string> result = new Result<string>();
            string funName = "【調用外部介面:微信企業消息推送介面】";
            try
            { 
                 //具體的業務邏輯---根據自身業務來寫
                MsgApiResult<WeiXinWorkMessageResponse> sendRet = await UPCHttpClientExtensions.PostData<MsgApiResult<WeiXinWorkMessageResponse>>(_client, _logger, "/api/weixin/work/messages/send", msgReq);

                if (sendRet != null)
                {
                    _logger.LogInformation($"{funName},{hrcodeStr}推送消息成功");
                    result.state = true;
                    result.data = sendRet.Return_data.MessageId;
                }
                else
                {
                    result.state = false;
                    result.error_msg = sendRet.Return_msg;
                    _logger.LogError($"{funName}:{hrcodeStr}推送消息失敗:{sendRet.Return_msg}");
                }

            }
            catch (Exception ex)
            {
                result.state = false;
                result.error_code = ErrorCode.OuterApiError;
                result.error_msg = funName + "調用外部介面異常:。" + ex.Message;
                _logger.LogError(ex, $"{funName}向{hrcodeStr}推送消息處理異常:{ex.Message}");
            }


            return result;

        }

針對HttpClient的Post方法我特意封裝了一個通用方法,如下:(可以根據自身項目自行改造)

 /// <summary>
   /// HttpClient擴展方法
   /// </summary>
    public class UPCHttpClientExtensions
    {
 
        /// <summary>
        /// httpclient-post方法的簡單處理,封裝成一個方法,便於調用
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="_client"></param>
        /// <param name="_logger"></param>
        /// <param name="actionUrl">http://xxx.com/後面之後的地址</param>
        /// <param name="param">一個對象</param>
        /// <param name="ContentType"></param>
        /// <param name="BearerToken"></param>
        /// <returns></returns>
        public async static  Task<T> PostData<T>(HttpClient _client, ILogger _logger,string actionUrl, dynamic param, string ContentType = "application/json", string BearerToken = "")
        {
            string funName = "PostData";
            string paramStr = JsonConvert.SerializeObject(param);
            string jrclientguid = Guid.NewGuid().ToString("n");
            try
            {
                _logger.LogInformation($"{funName}開始,url={_client.BaseAddress},action={actionUrl},postData={paramStr} ,jrclientguid={jrclientguid}---------");

                HttpResponseMessage response;
                using (HttpContent httpContent = new StringContent(paramStr, Encoding.UTF8))
                {
                    if (!string.IsNullOrWhiteSpace(BearerToken))
                    {
                        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", BearerToken);
                    }

                    httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(ContentType);

                    response = await _client.PostAsync(actionUrl, httpContent);

                }
                if (response != null && response.IsSuccessStatusCode)
                {
                    Type t = typeof(T);
                    if (typeof(T) == typeof(string))
                    {
                        string respStr = await response.Content.ReadAsStringAsync();
                        return (T)Convert.ChangeType(respStr, typeof(T));
                    }
                    else
                    {
                        string respStr = response.Content.ReadAsStringAsync().Result;
                        T resp = JsonConvert.DeserializeObject<T>(respStr);

                        return resp;
                    }
                }
                else
                {
                    return default(T);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex,$"{funName}報錯啦,url={_client.BaseAddress},action={actionUrl},postData={paramStr} ,jrclientguid={jrclientguid}---,ex={ex.Message}" );
                throw;
            }
            finally
            {
                _logger.LogInformation($"{funName}結束,url={_client.BaseAddress},action={actionUrl},postData={paramStr} ,jrclientguid={jrclientguid}---------");
            }


        }


    }

 

第4步:將服務註入容器:代碼在Startup類的ConfigureServices方法里:

 services.AddUpcMVC(Env)
               .AddUPCDbContext(Configuration)
.AddMsgApiClient(Configuration.GetSection(nameof(MsgApiClient)))//就是這行

 

第5步:使用:在Controller或其他地方都可以使用;

(1)首先在構造函數里註冊:

  private readonly ILogger _logger;
        private IMsgApiClient _IMsgApiClient;

        public HealthController(ILogger<HealthController> logger,    IMsgApiClient iMsgApiClient)
        {
            _logger = logger;
            _IMsgApiClient = iMsgApiClient;

        }

(2)調用方法:

 /// <summary>
        /// 發消息
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<Result<string>> POk()
        { 
            Result<string> result = new Result<string>();
            result = await _IMsgApiClient.SendWxWorkMsgAsync("100001002", "我是沐雪,請明天來我辦公室一趟!");
            //result.state = true;
            //result.data = "連接成功";

            return result;
        }

 


您的分享是我們最大的動力!

更多相關文章
  • ``` 包裝類型間的相等判斷應該用equals,而不是'!=' Inspection info: 所有的包裝類對象之間值的比較,全部使用equals方法比較。 說明:對於Integer var=?在-128至127之間的賦值,Integer對象是在IntegerCache.cache產生,會復用已有... ...
  • 數組轉換為集合 採用java中集合自帶的asList()方法就可以完成轉換了 特別註意: == 因為,Arrays.asList()方法轉換成的List集合類是java.util.Arrays.ArrayList下麵的,它是java.util.Arrays類中自己定義的一個內部類,沒有實現具體的ad ...
  • 關於java中遍歷map具體哪四種方式,請看下文詳解吧。 方式一 這是最常見的並且在大多數情況下也是最可取的遍歷方式。在鍵值都需要時使用。 方法二 在for each迴圈中遍歷keys或values。 如果只需要map中的鍵或者值,你可以通過keySet或values來實現遍歷,而不是用entryS ...
  • wxml內容: <view class="container"> <navigator class="search"> <icon type="search" size="13"></icon>搜索 </navigator> <view class="banner_box"> <swiper cla ...
  • 先上一個場景:假如你突然想做飯,但是沒有廚具,也沒有食材。網上購買廚具比較方便,食材去超市買更放心。 實現分析:在快遞員送廚具的期間,我們肯定不會閑著,可以去超市買食材。所以,在主線程裡面另起一個子線程去網購廚具。 但是,子線程執行的結果是要返回廚具的,而run方法是沒有返回值的。所以,這才是難點, ...
  • 創建線程的2種方式,一種是直接繼承Thread,另外一種就是實現Runnable介面。 這2種方式都有一個缺陷就是:在執行完任務之後無法獲取執行結果。 如果需要獲取執行結果,就必須通過共用變數或者使用線程通信的方式來達到效果,這樣使用起來就比較麻煩。 而自從Java 1.5開始,就提供了Callab ...
  • 一.利用多線程 直接new線程 使用線程池 二.採用Spring 的非同步方法去執行(無返回值) 在啟動類或者配置類加上 @EnableAsync 註解. 先把longTimeMethod 封裝到Spring的非同步方法中,這個方法一定要寫在Spring管理的類中,註意註解@Async @Async註解 ...
  • 問題:在多線程環境下,如何防止自己的變數被其它線程篡改 __ 答案:ThreadLocal. __ __ThreadLocal 不是用來解決共用對象的多線程訪問的競爭問題的,因為ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的。當線程終止 ...
一周排行
  • 本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/8135083.html,記錄一下學習過程以備後續查用。 一、引言 今天我們要講行為型設計模式的第九個模式--訪問者模式。如果按老規矩,先從名稱上來看這個模式,我根本不能獲得任何對理解該模式有用的信息, 而且這個 ...
  • 微信公眾號:【 "Dotnet9的博客" 】,網站:【 "Dotnet9" 】,問題或建議:【 "請網站留言" 】, 如果對您有所幫助:【 "歡迎贊賞" 】。 開源C WPF控制項庫系列: "(一)開源C WPF控制項庫《MaterialDesignInXAML》" "(二)開源C WPF控制項庫《Pan ...
  • 如今,當談到 WPF 時,我們言必稱 MVVM、框架(如 Prism)等,似乎已經忘了不用這些的話該怎麼使用 WPF 了。當然,這裡說的不用框架和 MVVM,並不是說像使用 Winform 那樣使用 WPF,而是追本溯源,重識 WPF 與生俱來的綁定和命令的風采。 ...
  • 本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/8176974.html,記錄一下學習過程以備後續查用。 一、引言 今天我們要講行為型設計模式的第十個模式--備忘錄模式,先從名稱上來看。備忘錄模式可以理解為對某個對象的狀態進行保存,等到需要恢復的時 候,可以從 ...
  • 前言 在兩年多以前就聽聞 Blazor 框架,是 .Net 之父的業餘實驗性項目,其目的是探索 .Net 與 WebAssembly 的相容性和應用前景。現在這個項目已經正式成為 Asp.Net Core 框架的一部分,公開了預覽版,官方教程也基本寫好上線了。就著這個機會,順便體驗一下這個框架用起來 ...
  • .NET web開發者在開發過程中,一定都踩過的坑,明明修改了js文件,可是部署到生產環境,客戶反饋說:“還是報錯啊”。。然後一臉懵逼的去伺服器上看文件,確實已經更新了。有經驗的coder可能就想到了,肯定是客戶端瀏覽器緩存搞的鬼。 此時會告訴客戶,請Crtl+F5刷新一下,這時,客戶會說:“Ctr ...
  • 哈嘍..大家好 很久沒有更新了,今天就來一篇最近開發用到的功能,那就是中英文切換,這個實際上也不是高大上,先說一下原理,在.NET Core框架中給我們提供了全球化的類,叫做Localization,其官方的文檔地址傳送門。 在我的項目中,我是這樣操作的,你想用別的方式,也可以看文檔自己去搞。這個已 ...
  • WPF允許使用Image元素顯示點陣圖。然而,按這種方法顯示圖片的方法完全是單向的。應用程式使用現成的點陣圖,讀取問題,併在視窗中顯示點陣圖。就其本身而言,Image元素沒有提供創建和編輯點陣圖信息的方法。 這正是WriteableBitmap類的用武之地。該類繼承自BitmapSource,BitmapS ...
  • 記錄LINQ學習過程。 概要 LINQ是一種“語言集成”的查詢表達式,使用LINQ可以智能提示和進行類型檢查。C#里可以編寫的LINQ查詢有SQL資料庫、XML文檔、ADO.NET數據集、支持IEnumerable和IEnumerable的對象。使用LINQ,可以簡單對數據源進行分組、排序、篩選。有 ...
  • 這兩天複習了下Request以及Response部分的內容。 主要內容 1. HTTP協議:響應消息 2. Request對象 3. Response對象 4. ServletContext對象 HTTP: 概念:Hyper Text Transfer Protocol 超文本傳輸協議 傳輸協議:定 ...
x