Taurus.MVC 微服務架構 入門開發教學:專案整合:6、微服務間的呼叫方式:Rpc.StartTaskAsync。

2022-09-15 18:00:46

系統目錄:

本系列分為專案整合、專案部署、架構演進三個方向,後續會根據情況調整文章目錄。

開源地址:https://github.com/cyq1162/Taurus.MVC

本系列第一篇:Taurus.MVC V3.0.3 微服務開源框架釋出:讓.NET 架構在大並行的演進過程更簡單。

Taurus.MVC 微服務架構 入門開發教學:專案整合:1、伺服器端:註冊中心、閘道器(提供可執行程式下載)。

Taurus.MVC 微服務架構 入門開發教學:專案整合:2、使用者端:ASP.NET Core(C#)專案整合:應用中心。

Taurus.MVC 微服務架構 入門開發教學:專案整合:3、使用者端:其它程式語言專案整合:Java整合應用中心。

Taurus.MVC 微服務架構 入門開發教學:專案整合:4、預設安全認證與自定義安全認證。

Taurus.MVC 微服務架構 入門開發教學:專案整合:5、統一的紀錄檔管理。

Taurus.MVC 微服務架構 入門開發教學:專案整合:6、微服務間的呼叫方式:Rpc.StartTaskAsync。

Taurus.MVC 微服務架構 入門開發教學:專案部署:1、微服務應用程式常規部署實現多開,節點擴容。

Taurus.MVC 微服務架構 入門開發教學:專案部署:2、讓Kestrel支援繫結多個域名轉發,替代Ngnix使用。

Taurus.MVC 微服務架構 入門開發教學:專案部署:3、微服務應用程式版本升級:全站升級和區域性模組升級。

Taurus.MVC 微服務架構 入門開發教學:專案部署:4、微服務應用程式釋出到Docker部署(上)。

Taurus.MVC 微服務架構 入門開發教學:專案部署:5、微服務應用程式釋出到Docker部署(下)。

Taurus.MVC 微服務架構 入門開發教學:專案部署:6、微服務應用程式Docker部署實現多開。

Taurus.MVC 微服務架構 入門開發教學:專案部署:7、微服務節點的監控。

Taurus.MVC 微服務架構 入門開發教學:架構演進:1、從單應用程式簡單過渡到負載均衡。

Taurus.MVC 微服務架構 入門開發教學:架構演進:2、負載均到模組拆分負載。

Taurus.MVC 微服務架構 入門開發教學:架構演進:3、模組拆分負載到多級負載均衡。

Taurus.MVC 微服務架構 入門開發教學:執行範例:https://github.com/cyq1162/Taurus.MVC.MicroService.Demo

前言:

以過多天的努力,終於提交了V3.1.1版本:

-------------------------V3.1.1.0【升級微服務功能】((2022-09-09 - 2022-09-13)-----------------------------
1、優化:調整Controller的名稱空間:Taurus.Core =>Taurus.Mvc
2、優化:控制器命名調整:允許控制器名稱不以Controller結尾(DefaultController除外)。
3、優化:Extend 更名:Plugin :原有Auth模組,獨立出外部專案,變更為外掛方式提供。
4、優化:微服務閘道器代理呼叫。
5、優化:微服務間的Key的網路呼叫請求頭傳引數名變更:microservice => mskey。
6、優化:CheckAck、CheckToken、CheckMicroService、BeginInvode、EndInvode等方法(引數優化)。

7、新增:IgnoreDefaultControllerAttribute 允許控制器忽略全域性DefaultController事件。
8、新增:提供微服務間的呼叫方式:Taurus.MicroService.Rpc。

而新的版本,重要的內容,就是重新提供了Rpc間的方法呼叫,花了不少精力在處理並行的優化上面。

對於微服務間的呼叫而言,Taurus.MVC微服務架構提供了以下方法:

namespace Taurus.MicroService
{
    /// <summary>
    /// 一般用於使用者端:服務間的RPC呼叫
    /// </summary>
    public static partial class Rpc
    {
        /// <summary>
        /// 根據微服務註冊名稱獲取請求的主機地址【有多個時,由內部控制負載均衡,每次獲取都會迴圈下一個】
        /// 【自動識別(先判斷:是否使用者端;再判斷:是否伺服器端)】
        /// </summary>
        /// <param name="name">微服務註冊名稱</param>
        /// <returns></returns>
        public static string GetHost(string name)

        /// <summary>
        /// 根據微服務註冊名稱獲取請求的主機地址【有多個時,由內部控制負載均衡,每次獲取都會迴圈下一個】
        /// </summary>
        /// <param name="name">微服務註冊名稱</param>
        /// <param name="isClient">指定查詢:true(使用者端):false(伺服器端)</param>
        /// <returns></returns>
        public static string GetHost(string name, bool isClient)

        /// <summary>
        /// 執行一個非同步的【通用】請求任務。
        /// </summary>
        /// <param name="request">任務請求</param>
        /// <returns></returns>
        public static RpcTask StartTaskAsync(RpcTaskRequest request)
        {
            return Rest.StartTaskAsync(request);
        }

        /// <summary>
        /// 對遠端服務發起一個非同步Get請求。
        /// </summary>
        /// <param name="name">遠端的註冊模組名</param>
        /// <param name="pathAndQuery">請求路徑和引數</param>
        /// <param name="header">請求頭</param>
        /// <returns></returns>
        public static RpcTask StartGetAsync(string name, string pathAndQuery, Dictionary<string, string> header = null)


        /// <summary>
        /// 對遠端服務發起一個非同步Get請求。
        /// </summary>
        /// <param name="url">請求的地址</param>
        /// <param name="header">可追加的請求頭部分</param>
        /// <returns></returns>
        public static RpcTask StartGetAsync(string url, Dictionary<string, string> header = null)

        /// <summary>
        ///  對遠端服務發起一個非同步Post請求。
        /// </summary>
        /// <param name="name">微服務名稱</param>
        /// <param name="pathAndQuery">請求路徑</param>
        /// <param name="data">請求資料</param>
        /// <param name="header">請求頭</param>
        /// <returns></returns>
        public static RpcTask StartPostAsync(string name, string pathAndQuery, byte[] data, Dictionary<string, string> header = null)

        /// <summary>
        /// 對遠端服務發起一個非同步Post請求。
        /// </summary>
        /// <param name="url">請求的地址</param>
        /// <param name="data">post的資料</param>
        /// <param name="header">可追加的請求頭部分</param>
        /// <returns></returns>
        public static RpcTask StartPostAsync(string url, byte[] data, Dictionary<string, string> header = null)

    }
}

下面進行使用說明:

1、Rpc.GetHost(...)方法說明:

對於微服務而言,微服務一開就是好多個,不太應該用固定的host去存取。

因此,框架提供方法,可以根據微服務的註冊名稱,獲得負載均衡下的其中一個host地址。

Rpc一般都在微服務應用中心(即使用者端呼叫);

伺服器端(閘道器,或註冊中心)一般不使用,但框架仍貼心的提供過載方法,以便伺服器端也可以使用。

框架提供此方法,獲得請求Host後,使用者是可以根據情況,自行寫程式碼呼叫,也可以使用以下框架封裝好的程式碼。

2、RpcTask:方法間的呼叫(非同步任務)

框架對ASP.NET、ASP.NET2.1-3.1-5、ASP.NET6及以上,分三種情景分別進行了優化封裝。

並嚴格進行了並行(>1000)下的測試與優化,保障了並行下的順暢不卡頓。

下面看使用範例方法:

1、Get請求:

    public class RpcController : Taurus.Mvc.Controller
    {
        static int i = 0;
        public void CallGet()
        {
            i++;
            RpcTask task = Rpc.StartGetAsync("ms", "/ms/hello?msg=" + DateTime.Now.Ticks);
            System.Diagnostics.Debug.WriteLine("-----------------------------" + i);
            //可以處理其它業務邏輯
            string text = task.Result.IsSuccess ? task.Result.ResultText : task.Result.ErrorText;
            RpcTaskState state = task.State;
            if (string.IsNullOrEmpty(text) || !task.Result.IsSuccess)
            {
                Response.StatusCode = 404;
            }
            Write(text);
        }
}

說明:

1、task.Wait(...)方法,可以手動執行非同步等待。

2、task.Result(獲取結果,內部也會進行等待返回)

2、Post請求:

public void CallPost()
        {

            i++;
            RpcTask task = Rpc.StartPostAsync("ms", "/ms/hello?id=" + DateTime.Now.Ticks, Encoding.UTF8.GetBytes("id=2&msg=" + DateTime.Now.Ticks));//,
            System.Diagnostics.Debug.WriteLine("-----------------------------" + i);
            task.Wait();
            string text = task.Result.IsSuccess ? task.Result.ResultText : task.Result.ErrorText;
            RpcTaskState state = task.State;
            if (string.IsNullOrEmpty(text) || !task.Result.IsSuccess)
            {
                Response.StatusCode = 404;
            }
            Write(text);
        }

3、Put請求:

        public void CallPut()
        {
            i++;
            RpcTask task = Rpc.StartTaskAsync(new RpcTaskRequest() { Method = "Put", Url = Rpc.GetHost("ms") + "/ms/hello2" });//,Encoding.UTF8.GetBytes("id=2&msg=" + DateTime.Now.Ticks)
            System.Diagnostics.Debug.WriteLine("-----------------------------" + i);
            task.Wait();
            string text = task.Result.IsSuccess ? task.Result.ResultText : task.Result.ErrorText;
            RpcTaskState state = task.State;
            if (string.IsNullOrEmpty(text) || !task.Result.IsSuccess)
            {

                Response.StatusCode = 404;
            }
            Write(text);
        }

4、Head請求:

        public void CallHead()
        {
            i++;
            RpcTask task = Rpc.StartTaskAsync(new RpcTaskRequest() { Method = "Head", Url = Rpc.GetHost("ms") + "/ms/hello2" });//,Encoding.UTF8.GetBytes("id=2&msg=" + DateTime.Now.Ticks)
            System.Diagnostics.Debug.WriteLine("-----------------------------" + i);
            task.Wait();
            string text = task.Result.IsSuccess ? CYQ.Data.Tool.JsonHelper.ToJson(task.Result.Header) : task.Result.ErrorText;
            RpcTaskState state = task.State;
            if (string.IsNullOrEmpty(text) || !task.Result.IsSuccess)
            {
                Response.StatusCode = 404;
            }
            Write(text);
        }

5、Delete請求:

        public void CallDelete()
        {

            i++;
            RpcTask task = Rpc.StartTaskAsync(new RpcTaskRequest() { Method = "Delete", Url = Rpc.GetHost("ms") + "/ms/hello2" });//,Encoding.UTF8.GetBytes("id=2&msg=" + DateTime.Now.Ticks)
            System.Diagnostics.Debug.WriteLine("-----------------------------" + i);
            task.Wait();
            string text = task.Result.IsSuccess ? task.Result.ResultText : task.Result.ErrorText;
            RpcTaskState state = task.State;
            if (string.IsNullOrEmpty(text) || !task.Result.IsSuccess)
            {

                if (state == RpcTaskState.Running)
                {
                    task.Wait();
                }
                Response.StatusCode = 404;
            }
            Write(text);
        }

3、測試執行準備:

 為了測試,程式啟動了:

1、註冊中心:(監聽5000埠)

2、微服務模組(ms)模組:啟動了兩個:(隨機埠)

3、微服務模組(rpc)模組,啟動了1個:(監聽4000埠)

範例是在rpc模組中,呼叫ms模組的服務: 程式碼即上述RpcTask中的CallGet的程式碼。 

4、測試執行結果:

Rpc呼叫:請求範例結果:(瀏覽器顯示是2毫秒) 

Rpc模組請求時間:(控制檯顯示的請求時間,1.N毫秒左右):

MS模組(被呼叫者)請求時間:(ms模組執行業務請求的時間,每次都在1毫秒以內

總結:

1、框架Rpc架基於WebClient和HttpClient進行優化,主要優化點是進行埠和連結的複用。 

2、效能整體在預期內,在並行下(>1000)仍然穩定流暢。

3、使用簡單。