本系列分為專案整合、專案部署、架構演進三個方向,後續會根據情況調整文章目錄。
開源地址: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、使用簡單。