在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务
在 ASP.NET Core Web API中使用 Polly 構(gòu)建彈性容錯的微服務(wù)
https://procodeguide.com/programming/polly-in-aspnet-core/
在本文中,我們將了解如何在微服務(wù)中實(shí)現(xiàn)彈性容錯,即在 ASP.NET Core 中使用 Polly 構(gòu)建彈性微服務(wù)(Web API)。通過在微服務(wù)中實(shí)現(xiàn)容錯,我們確保在其中一項服務(wù)發(fā)生任何故障時,整個系統(tǒng)不會受到影響。
在本文中,我不會介紹如何在 ASP.NET Core 中構(gòu)建微服務(wù),因為我已經(jīng)在我的另一篇關(guān)于使用 ASP.NET Core[1]的微服務(wù)的文章中詳細(xì)介紹了這一點(diǎn)。在這里,我們將看到如何在 ASP.NET Core 中使用 Polly 在微服務(wù)中實(shí)現(xiàn)容錯。
無論您是在處理微服務(wù)還是單體應(yīng)用程序,很有可能需要調(diào)用外部第三方或內(nèi)部 API,因此您需要以一種可以處理該 API 故障的方式構(gòu)建您的代碼作為您的應(yīng)用程序流取決于來自該 API 的響應(yīng)。
在應(yīng)用程序或彈性 Web 服務(wù)中構(gòu)建彈性意味著我們需要確保 Web 服務(wù)始終可用且具有可接受的功能,即使在服務(wù)高負(fù)載、網(wǎng)絡(luò)故障、我們的服務(wù)所依賴的其他服務(wù)出現(xiàn)故障等情況下,等等。
ASP.NET Core 中的 Polly目錄
什么是 Polly,我們?yōu)槭裁葱枰?#xff1f;
處理部分故障的設(shè)計原則
Polly 支持的彈性策略
重試
斷路器
超時
Bulkhead(隔板)
緩存
回退
策略封裝
在 ASP.NET Core 中實(shí)現(xiàn) Polly 的策略
演示的總體方法
創(chuàng)建 ASP.NET Core Web API 項目
創(chuàng)建客戶服務(wù)
創(chuàng)建訂單服務(wù)
在 ASP.NET Core 訂單服務(wù)中配置 Polly 的策略
重試策略
超時策略
回退策略
斷路器策略
Bulkhead策略
概括
下載源代碼
什么是 Polly,我們?yōu)槭裁葱枰?#xff1f;
Polly[2]是一個 .NET 彈性和瞬態(tài)故障處理庫,允許開發(fā)人員以流暢和線程安全的方式表達(dá)重試、斷路器、超時、隔板隔離和回退等策略。
如果我們說我們已經(jīng)徹底測試了我們的應(yīng)用程序并且生產(chǎn)環(huán)境中不會出現(xiàn)任何中斷,那我們就完全錯了。由于應(yīng)用程序崩潰、響應(yīng)緩慢、系統(tǒng)負(fù)載過大、硬件故障、網(wǎng)絡(luò)問題等等,將會出現(xiàn)應(yīng)用程序故障。
為了在我們的應(yīng)用程序中處理這些故障,首先我們必須承認(rèn)這些故障會發(fā)生,其次我們必須在我們的應(yīng)用程序中加入容錯,即我們確保整個系統(tǒng)不會由于一個或多個服務(wù)故障而失敗。
例如,微服務(wù)是一種設(shè)計,其中一個大型應(yīng)用程序被開發(fā)為一組具有自己的數(shù)據(jù)存儲的小型獨(dú)立服務(wù)。通過在微服務(wù)中構(gòu)建容錯,我們以這樣一種方式設(shè)計它,即一個服務(wù)的故障不會影響其他服務(wù)的工作,即如果與配置文件更新相關(guān)的服務(wù)關(guān)閉,那么用戶應(yīng)該無法更新配置文件,但其他事務(wù)如訂單輸入\查詢應(yīng)該可以正常工作。
此外,構(gòu)建彈性服務(wù)不是為了避免故障,而是關(guān)于從故障中恢復(fù)并以避免停機(jī)和數(shù)據(jù)丟失的方式執(zhí)行其功能的能力。微服務(wù)應(yīng)設(shè)計為處理部分故障。如果您不設(shè)計和實(shí)施確保容錯的技術(shù),即使是部分故障也可能被放大。
在 ASP.NET Core 中使用 Polly 只需幾行代碼,我們就可以構(gòu)建具有彈性的應(yīng)用程序,盡管在復(fù)雜的微服務(wù)或基于云的部署中發(fā)生部分故障,但仍能順利運(yùn)行。在 ASP.NET Core 中使用 Polly 在微服務(wù)中實(shí)現(xiàn)容錯后,我們確保如果服務(wù)失敗或宕機(jī),整個系統(tǒng)不會宕機(jī)。
使用 ASP.NET Core 中的 Polly 策略,我們可以設(shè)計我們的應(yīng)用程序以在出現(xiàn)故障時以指定的方式進(jìn)行響應(yīng)。
處理部分故障的設(shè)計原則
以下是推薦用于處理微服務(wù)中的部分故障的一些設(shè)計原則列表
通訊訂閱
強(qiáng)烈建議使用異步通信,而不是跨內(nèi)部微服務(wù)的長鏈同步 HTTP 調(diào)用。唯一的同步調(diào)用應(yīng)該是客戶端應(yīng)用程序和入門級微服務(wù)或 API 網(wǎng)關(guān)之間的前端調(diào)用。
可以通過在服務(wù)調(diào)用中實(shí)現(xiàn)重試來避免間歇性網(wǎng)絡(luò)或通道故障。這些重試應(yīng)該是有限的次數(shù),不能是無限的。
始終為每個網(wǎng)絡(luò)調(diào)用實(shí)現(xiàn)超時。調(diào)用客戶端不應(yīng)無休止地等待來自任何服務(wù)的響應(yīng),而應(yīng)等待預(yù)定義的時間限制,一旦該時間過去,則調(diào)用失敗。
使用斷路器模式,對失敗的服務(wù)進(jìn)行重試,如果服務(wù)仍然失敗,則在一些固定的重試之后,斷路器跳閘,以便進(jìn)一步的嘗試立即失敗,即不會對失敗的服務(wù)進(jìn)行新的調(diào)用,而不是它將假定其失敗或已關(guān)閉。有一個時間限制,不會對失敗的服務(wù)進(jìn)行新的調(diào)用,一旦時間過去,新的調(diào)用將轉(zhuǎn)到失敗的服務(wù)以驗證服務(wù)是否再次啟動和運(yùn)行。如果新請求成功,則斷路器將關(guān)閉,請求將轉(zhuǎn)發(fā)到服務(wù)。
為失敗的服務(wù)提供一些回退或默認(rèn)行為,即如果服務(wù)請求失敗,則提供一些回退邏輯,如返回緩存數(shù)據(jù)或默認(rèn)數(shù)據(jù)。這可以解決難以實(shí)現(xiàn)插入和更新的查詢。
對于調(diào)用(客戶端)的兩個微服務(wù)之間的通信,微服務(wù)應(yīng)該對來自特定服務(wù)的待處理請求數(shù)量實(shí)施一些限制,即如果已達(dá)到限制,則向同一服務(wù)發(fā)送額外請求可能毫無意義,而是其他請求應(yīng)立即失敗。
Polly 支持的彈性策略
這是 Polly 在 ASP.NET Core 中支持的彈性策略列表:
重試
ASP.NET Core ?Polly 的這個策略允許我們在調(diào)用服務(wù)時配置自動重試。
假設(shè)我們有一個訂單服務(wù),它調(diào)用產(chǎn)品服務(wù)來獲取所訂購商品的詳細(xì)信息。現(xiàn)在,如果產(chǎn)品服務(wù)具有大部分時間有效但有時會失敗的隨機(jī)行為。
現(xiàn)在在這種情況下,如果訂單服務(wù)收到來自產(chǎn)品服務(wù)的失敗響應(yīng),那么重試請求可能會從產(chǎn)品服務(wù)中獲取結(jié)果。
Polly 幫助我們實(shí)現(xiàn)了這個重試策略,限制了從訂單服務(wù)到產(chǎn)品服務(wù)的最大重試次數(shù)。
斷路器
當(dāng)服務(wù)請求失敗計數(shù)超過某個預(yù)先配置的閾值時,ASP.NET Core 中的 Polly 策略幫助我們打破鏈路,即在配置的時間段內(nèi)阻止服務(wù)請求的執(zhí)行。
我們將采用相同的訂單服務(wù)示例,向產(chǎn)品服務(wù)請求商品詳細(xì)信息。現(xiàn)在假設(shè)從訂單服務(wù)到產(chǎn)品服務(wù)的請求即使在重試時也不斷失敗,那么在這種情況下,我們阻止調(diào)用產(chǎn)品服務(wù)并提供緩存或默認(rèn)數(shù)據(jù)。
這種在服務(wù)失敗達(dá)到配置次數(shù)時不調(diào)用服務(wù)并依靠回退機(jī)制的設(shè)計稱為斷路器。當(dāng)訂單服務(wù)成功連續(xù)調(diào)用產(chǎn)品服務(wù)時,我們說鏈路關(guān)閉(關(guān)閉狀態(tài))。但是當(dāng)訂單服務(wù)不調(diào)用產(chǎn)品服務(wù)并依賴回退機(jī)制時,在這種情況下,我們說鏈路是開放的(開放狀態(tài)
超時
ASP.NET Core 中 Polly 的這個策略允許我們在對另一個服務(wù)的 HTTP 請求期間實(shí)現(xiàn)超時,以確保調(diào)用者服務(wù)不必等待超時。
當(dāng)訂單服務(wù)調(diào)用產(chǎn)品服務(wù)獲取商品詳細(xì)信息時,如果產(chǎn)品服務(wù)的響應(yīng)延遲(產(chǎn)品服務(wù)可能正在等待來自慢速/掛起數(shù)據(jù)庫的響應(yīng)),那么訂單服務(wù)會假設(shè)產(chǎn)品服務(wù)在超時時間之外不太可能成功。因此,超出超時期限的訂單服務(wù)假定產(chǎn)品服務(wù)存在問題,它將停止等待產(chǎn)品服務(wù)的響應(yīng)并采取適當(dāng)?shù)拇胧?/p>
隔板隔離
ASP.NET Core 中的 Polly 策略允許我們限制應(yīng)用程序的任何部分可以消耗的資源總量,這樣應(yīng)用程序的失敗部分就不會導(dǎo)致級聯(lián)故障也導(dǎo)致應(yīng)用程序的其他部分癱瘓。
當(dāng)訂單服務(wù)調(diào)用產(chǎn)品服務(wù)以獲取商品詳細(xì)信息時,如果由于某些原因?qū)е庐a(chǎn)品服務(wù)不可用,那么請求將開始備份訂單服務(wù),并可能導(dǎo)致訂單服務(wù)性能下降甚至導(dǎo)致訂單服務(wù)崩潰。
Bulkhead Isolation 有助于隔離部分應(yīng)用程序并控制內(nèi)存、CPU、套接字、線程等的使用,以便如果您的應(yīng)用程序的某一部分工作不順利,此策略將防止該部分影響或停止整個應(yīng)用程序。
它還允許您指定可以執(zhí)行的并發(fā)請求數(shù)量以及可以排隊執(zhí)行的請求數(shù)量,以便一旦并發(fā)和隊列插槽已滿,新請求將立即失敗。
緩存
ASP.NET Core 中 Polly 中的此策略允許在第一次檢索響應(yīng)時自動將響應(yīng)存儲在緩存中(在內(nèi)存或分布式緩存中),以便可以從緩存中返回對同一資源的后續(xù)請求。
當(dāng)訂單服務(wù)為商品詳情調(diào)用產(chǎn)品服務(wù)時,訂單服務(wù)可以將商品詳情存儲在緩存中,以便可以從緩存中獲取相同產(chǎn)品的下一個請求,而不是再次調(diào)用產(chǎn)品服務(wù)對于相同的產(chǎn)品。
回退
ASP.NET Core 中 Polly 中的此策略允許我們提供替代路徑,即可以返回的值或可以采取的操作,以防被調(diào)用的服務(wù)關(guān)閉,即返回錯誤或發(fā)生超時。
當(dāng)訂單服務(wù)調(diào)用產(chǎn)品服務(wù)以獲取商品詳細(xì)信息時,如果對產(chǎn)品服務(wù)的請求失敗,則配置的回退將允許訂單服務(wù)決定在產(chǎn)品服務(wù)失敗的情況下如何處理。訂單服務(wù)可以返回默認(rèn)數(shù)據(jù)或根據(jù)失敗采取一些行動。
無論您重試多少次,都必然會發(fā)生故障,因此您需要計劃在發(fā)生故障時應(yīng)該做什么。回退通常與重試、斷路器等其他策略結(jié)合使用。
封裝策略( Policy Wrap)
ASP.NET Core 中 Polly 中的此策略允許靈活組合 Polly 中支持的任何策略,從而能夠組合彈性策略。會有不同類型的故障需要不同的策略,我們可以根據(jù)故障類型應(yīng)用策略組合。
簡而言之,當(dāng)您想同時使用多個策略時,請使用 Policy Wrap。
現(xiàn)在讓我們看看如何在 ASP.NET Core 中實(shí)現(xiàn) Polly 支持的這些策略。
在 ASP.NET Core 中實(shí)現(xiàn) Polly 的策略
演示的總體方法
以下是此演示所采用的完整方法的詳細(xì)信息
1.我們將為客戶微服務(wù)創(chuàng)建第一個 ASP.NET Core Web API 項目,該項目包含一個 Get 操作方法以返回給定客戶代碼的客戶名稱2.我們將為訂單微服務(wù)添加第二個 ASP.NET Core Web API 項目,該項目包含一個 Get 操作方法來返回客戶的訂單詳細(xì)信息。3.除了訂單詳細(xì)信息,此訂單服務(wù)還返回客戶名稱。要獲取此客戶名稱訂單服務(wù),請調(diào)用客戶服務(wù) get 方法。4.我們已經(jīng)實(shí)現(xiàn)了這個從訂單服務(wù)到客戶服務(wù)的 HTTP 調(diào)用來獲取客戶名稱。5.我們將在向客戶服務(wù)發(fā)出 HTTP 請求的同時,在訂單服務(wù)中實(shí)施和測試各種 Polly 策略。6.我們將模擬客戶服務(wù)的故障,并了解如何通過使用 ASP.NET Core 中的 Polly 策略讓我們的訂單服務(wù)容錯。讓我們首先創(chuàng)建所需的 ASP.NET Core Web API 類型的項目,該項目將用于演示如何在 ASP.NET Core 中使用 Polly 的策略
創(chuàng)建 ASP.NET Core Web API 項目
為了演示 Polly 策略在 ASP.NET Core 中的實(shí)現(xiàn),我們將創(chuàng)建幾個 ASP.NET Core Web API 項目并按照下面指定的詳細(xì)信息配置它們。
創(chuàng)建客戶服務(wù)
創(chuàng)建一個名為 ProCodeGuide.Polly.Customer 的 ASP.NET Core Web API 類型的新項目
創(chuàng)建項目后,默認(rèn)的 WeatherForecast 控制器已被刪除,因為演示不需要它。
添加客戶控制器
我們需要添加一個 Customer 控制器,該控制器將有一個 get 操作方法,該方法根據(jù)輸入的客戶代碼返回客戶名稱。我們將添加 Controllers\CustomerController.cs 如下所示
[Route("api/[controller]")] [ApiController] public class CustomerController : ControllerBase {private Dictionary<int, string> _customerNameDict = null;public CustomerController(){if(_customerNameDict == null){_customerNameDict = new Dictionary<int, string>();_customerNameDict.Add(1, "Pro Code Guide");_customerNameDict.Add(2, "Support - Pro Code Guide");_customerNameDict.Add(3, "Sanjay");_customerNameDict.Add(4, "Sanjay - Pro Code Guide");}}[HttpGet][Route("GetCustomerName/{customerCode}")]public ActionResult<string> GetCustomerName(int customerCode){if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode)){return _customerNameDict[customerCode];}return "Customer Not Found";} }出于演示目的,我在控制器本身中硬編碼了客戶代碼和名稱列表,但理想情況下,這些數(shù)據(jù)應(yīng)該來自使用實(shí)體框架的數(shù)據(jù)庫。
運(yùn)行并測試客戶服務(wù)
從 Visual Studio 構(gòu)建和運(yùn)行應(yīng)用程序后,您應(yīng)該會從 swagger (OpenAPI) 看到以下屏幕。
在執(zhí)行獲取操作 /api/Customer/GetCustomerName/2 時,您應(yīng)該從操作方法中獲得以下響應(yīng)。
創(chuàng)建訂單服務(wù)
在同一個解決方案中創(chuàng)建 ASP.NET Core Web API 類型的第二個項目,名稱為 ProCodeGuide.Polly.Order
創(chuàng)建 ASP.NET Core Web API 訂單
創(chuàng)建項目后,默認(rèn)的 WeatherForecast 控制器已被刪除,因為演示不需要它。
添加模型
首先在 Models\Item.cs & Models\OrderDetails.cs 中添加 Order details 所需的模型,如下所示
public class Item {public int Id { get; set; }public string Name { get; set; } } public class OrderDetails {public int Id { get; set; }public string CustomerName { get; set; }public DateTime SetupDate { get; set; }public List<Item> Items { get; set; } }添加訂單控制器
我們需要添加一個 Order 控制器,它將有一個 get action 方法,該方法根據(jù)輸入的客戶代碼返回詳細(xì)的訂單。此方法還將對客戶服務(wù)進(jìn)行 HTTP 調(diào)用以獲取客戶代碼的客戶名稱。
讓我們首先在依賴容器中添加 httpclient 服務(wù),以便我們可以在訂單控制器中獲取該對象 httpclient 以對客戶服務(wù)進(jìn)行 HTTP 調(diào)用。要在依賴容器中添加 httpclient 服務(wù),請將以下行添加到 Startup.cs 中的 ConfigureServices 方法
services.AddHttpClient();我們將添加 Controllers\OrderController.cs 如下所示
[Route("api/[controller]")] [ApiController] public class OrderController : ControllerBase {private readonly ILogger<OrderController> _logger;private readonly IHttpClientFactory _httpClientFactory;private HttpClient _httpClient;private string apiurl = @"http://localhost:23833/";private OrderDetails _orderDetails = null;public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory){_logger = logger;_httpClientFactory = httpClientFactory;if (_orderDetails == null){_orderDetails = new OrderDetails{Id = 7261,SetupDate = DateTime.Now.AddDays(-10),Items = new List<Item>()};_orderDetails.Items.Add(new Item{Id = 6514,Name = ".NET Core Book"});}}[HttpGet][Route("GetOrderByCustomer/{customerCode}")]public OrderDetails GetOrderByCustomer(int customerCode){_httpClient = _httpClientFactory.CreateClient();_httpClient.BaseAddress = new Uri(apiurl);var uri = "/api/Customer/GetCustomerName/" + customerCode;var result = _httpClient.GetStringAsync(uri).Result;_orderDetails.CustomerName = result;return _orderDetails;} }apiurl?– 是客戶服務(wù)的 URL(主機(jī)和端口號)
出于演示目的,我對訂單詳細(xì)信息進(jìn)行了硬編碼,即所有客戶的相同訂單詳細(xì)信息,但理想情況下,這些數(shù)據(jù)應(yīng)該來自使用實(shí)體框架的數(shù)據(jù)庫。
使用 Serilog 啟用文件日志記錄
接下來檢查添加 Polly 策略后代碼的行為,我們將添加對 Serilog 日志記錄的支持以記錄到代碼中的文件。
使用包管理器控制臺將以下包安裝到項目中
Install-Package Serilog.AspNetCore Install-Package Serilog.Settings.Configuration Install-Package Serilog.Sinks.File將 Serilog 的配置添加到 appsettings.json 文件中,如下所示
"Serilog": {"MinimumLevel": "Information","Override": {"Microsoft.AspNetCore": "Information"},"WriteTo": [{"Name": "File","Args": {"path": "Serilogs\\AppLogs.log"}}] }在 Program.cs 文件中的 CreateHostBuilder 方法中配置 Serilog,如下代碼所示
public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).UseSerilog().ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseStartup<Startup>();});在 Startup.cs 文件中的 Startup Constructor 中配置 Serilog,如下代碼所示
public Startup(IConfiguration configuration) {Configuration = configuration;Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(configuration).CreateLogger(); }以上配置會生成日志到路徑{Project Path}\Serilogs\AppLogs.log下的文件
如果您想進(jìn)一步詳細(xì)了解如何將 Serilog 日志添加到項目中,那么您可以在此處[3]查看我的詳細(xì)文章
現(xiàn)在我們已經(jīng)添加了所需的項目并配置了項目,讓我們運(yùn)行并檢查項目。由于此訂單服務(wù)依賴于客戶服務(wù),因此我們需要確保在測試時兩個項目都已啟動并正在運(yùn)行。為了從 Visual Studio 一起啟動這兩個項目,我們將對啟動項目進(jìn)行更改。
右鍵單擊解決方案資源管理器中的解決方案文件,然后選擇將加載屬性屏幕的屬性,您可以通過選擇多個啟動項目選項來配置以同時啟動兩個項目,如下所示
現(xiàn)在,當(dāng)您從 Visual Studio 運(yùn)行項目時,訂單和客戶服務(wù)項目都將啟動。
運(yùn)行并測試訂單服務(wù)
從 Visual Studio 構(gòu)建和運(yùn)行應(yīng)用程序后,您應(yīng)該會從 swagger (OpenAPI) 看到以下屏幕。
在執(zhí)行獲取操作 /api/Order/GetOrderByCustomer/2 時,您應(yīng)該從操作方法中獲得以下響應(yīng)。
現(xiàn)在讓我們看看當(dāng)客戶服務(wù)不可用時會發(fā)生什么,即訂單服務(wù)沒有問題但客戶服務(wù)沒有啟動和運(yùn)行。為了模擬這種情況,我剛剛啟動了 Order 服務(wù),但沒有啟動客戶服務(wù),因此客戶服務(wù)沒有啟動和運(yùn)行。
正如我們在上面看到的,當(dāng)客戶服務(wù)沒有啟動并運(yùn)行時,訂單服務(wù)也會開始拋出錯誤。從 Serilog 中,您將能夠看到訂單服務(wù)向客戶服務(wù)發(fā)出請求,該請求返回了異常,因此在級聯(lián)效果中,訂單服務(wù)也返回了 500
讓我們探討如何使用 ASP.NET Core 中的 Polly 策略來避免這種行為
在 ASP.NET Core 訂單服務(wù)中配置 Polly 的策略
要在 ASP.NET Core 中配置 Polly 的策略,您需要在項目中安裝 Polly 包。您可以通過在包管理器控制臺窗口中運(yùn)行以下命令來添加 Polly 包
Install-Package Polly現(xiàn)在我們在訂單服務(wù)項目中安裝了 Polly 包文件,讓我們看看如何在我們的 ASP.NET Core Web API(訂單服務(wù))項目中使用 Polly 的策略來使我們的訂單服務(wù)容錯,盡管客戶服務(wù)沒有運(yùn)行或失敗。
聲明 Polly 策略的方法不止一種,即使用注冊表或通過 Startup 添加它們。但是,為了在這篇介紹文章中保持簡單,我們將直接在構(gòu)造函數(shù)的控制器類中創(chuàng)建 Polly 策略。
重試策略
根據(jù) name 的定義,此策略建議您需要在第一次嘗試失敗的情況下重試請求。現(xiàn)在,這些重試必須是固定的次數(shù),因為這種重試業(yè)務(wù)不可能永遠(yuǎn)持續(xù)下去。此重試策略可讓您配置要進(jìn)行的重試次數(shù)。
此重試策略允許在重試之前添加延遲,或者在對失敗的服務(wù)進(jìn)行重試調(diào)用之前不要等待,因此如果您希望服務(wù)中返回錯誤的問題將立即得到糾正,那么您應(yīng)該只實(shí)現(xiàn)重試邏輯而不任何延誤。
考慮從訂單服務(wù)到客戶服務(wù)的 HTTP 請求失敗的場景。來自客戶服務(wù)的這個錯誤可能是永久性的,也可能是暫時的。要處理臨時故障,您需要添加邏輯以將請求重試至客戶服務(wù)至少 2 次,以確保使用重試處理來自客戶服務(wù)的臨時故障。
按照這個重試邏輯,訂單服務(wù)將向客戶服務(wù)請求客戶名稱,如果客戶服務(wù)返回異常,那么訂單服務(wù)仍將再向客戶服務(wù)重試請求 2 次,然后得出結(jié)論,現(xiàn)在現(xiàn)在不可能了獲得客戶服務(wù)的成功回復(fù)。
要模擬來自客戶服務(wù)的隨機(jī)故障,請將以下操作方法添加到客戶服務(wù)中。這種方法是隨機(jī)返回數(shù)據(jù)或錯誤。為了實(shí)現(xiàn)這種隨機(jī)行為,我們生成一個 1 到 10 之間的數(shù)字,如果這個生成的數(shù)字是偶數(shù),那么我們將返回一個帶有 HTTP 狀態(tài)代碼 500 的服務(wù)器錯誤,如果生成的數(shù)字不是偶數(shù),即它是奇數(shù),那么我們將返回根據(jù)客戶代碼使用客戶名稱的成功響應(yīng)。
因此,此客戶服務(wù)操作方法 GetCustomerNameWithTempFailure 將隨機(jī)運(yùn)行,即有時會返回錯誤或在某些情況下會返回成功響應(yīng)
[HttpGet] [Route("GetCustomerNameWithTempFailure/{customerCode}")] public ActionResult<string> GetCustomerNameWithTempFailure(int customerCode) {try{Random rnd = new Random();int randomError = rnd.Next(1, 11); // creates a number between 1 and 10if (randomError % 2 == 0)throw new Exception();if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode)){return _customerNameDict[customerCode];}return "Customer Not Found";}catch{//Log Errorreturn StatusCode(StatusCodes.Status500InternalServerError);} }要在 ASP.NET Core 中使用 Polly 實(shí)現(xiàn)重試邏輯,我們需要聲明 RetryPolicy 類型的對象并定義策略,如下面的代碼所示
//Remaining Code has been removed for readabilityprivate readonly RetryPolicy _retryPolicy;public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory) {//Remaining Code has been removed for readability_retryPolicy = Policy.Handle<Exception>().Retry(2); }上面的代碼示例將創(chuàng)建一個重試策略,如果 HTTP 服務(wù)調(diào)用失敗并且策略處理異常,該策略將重試最多兩次。在這里,我們指定了重試策略處理通用異常,因此它將重試所有類型的異常,但您甚至可以為更具體的異常(如 HttpRequestException)配置重試策略,然后它只會重試類型為 HttpRequestException 的異常。
接下來,我們將在訂單服務(wù)中添加一個新的操作方法,它將利用 RetryPolicy 對象向客戶服務(wù)的新操作方法 (GetCustomerNameWithTempFailure) 發(fā)出 HTTP 請求,該方法隨機(jī)返回錯誤。重試策略用于處理來自客戶服務(wù)的隨機(jī)故障。
[HttpGet] [Route("GetOrderByCustomerWithRetry/{customerCode}")] public OrderDetails GetOrderByCustomerWithRetry(int customerCode) {_httpClient = _httpClientFactory.CreateClient();_httpClient.BaseAddress = new Uri(apiurl);var uri = "/api/Customer/GetCustomerNameWithTempFailure/" + customerCode;var result = _retryPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);_orderDetails.CustomerName = result;return _orderDetails; }RetryPolicy 對象使用委托在 Execute() 委托中對客戶服務(wù)執(zhí)行所需的 HTTP 調(diào)用。如果 HTTP 調(diào)用引發(fā)由重試策略處理的異常,則 HTTP 調(diào)用將重試配置的次數(shù)。
讓我們在 ASP.NET Core 中運(yùn)行和測試 Polly 的重試策略。在 Visual Studio 中運(yùn)行解決方案后,兩個項目(即客戶和訂單)都應(yīng)該啟動。兩個服務(wù)都開始轉(zhuǎn)到訂購服務(wù)后,您應(yīng)該會看到以下來自 swagger (OpenAPI) 的屏幕
在上面的屏幕上選擇操作 /api/Order/GetOrderByCustomerWithRetry/(customerCode) 它應(yīng)該展開,然后單擊 Try it out 按鈕。之后,您應(yīng)該會看到以下屏幕,您需要在其中輸入客戶代碼的值并單擊執(zhí)行按鈕。
如上所示,單擊執(zhí)行后,我們根據(jù)為客戶代碼輸入的值獲得了正確客戶名稱的成功響應(yīng)。
但是訂單服務(wù)中的 GetOrderByCustomerWithRetry 操作正在對客戶服務(wù)進(jìn)行 HTTP 調(diào)用,該調(diào)用會隨機(jī)返回錯誤,因此讓我們檢查日志,看看在客戶服務(wù)中對 GetCustomerNameWithTempFailure 的 HTTP 調(diào)用期間發(fā)生了什么
正如我們在上面日志的屏幕截圖中看到的,當(dāng)我們從訂單服務(wù)調(diào)用客戶服務(wù)時,第一次調(diào)用返回了一個錯誤,但由于我們已經(jīng)配置了重試策略并且它被重試,并且在第一次重試時客戶服務(wù)返回成功響應(yīng)并帶有正確的客戶名稱根據(jù)客戶代碼的價值。因此,通過在訂單服務(wù)中使用重試策略,我們能夠處理客戶服務(wù)中的臨時故障。
超時策略
根據(jù) name 的定義,此策略建議您需要在設(shè)置的時間限制內(nèi)沒有來自其他服務(wù)的響應(yīng)的情況下終止請求。
考慮從訂單服務(wù)到客戶服務(wù)的 HTTP 請求被延遲的場景。來自客戶服務(wù)的此錯誤可能永無止境,因為客戶服務(wù)可能正在等待來自慢速/掛起數(shù)據(jù)庫的響應(yīng)或來自第三方服務(wù)的響應(yīng),并且客戶服務(wù)尚未為這些調(diào)用實(shí)現(xiàn)超時。
要處理延遲響應(yīng),您需要添加邏輯以在設(shè)定的時間限制過后對客戶服務(wù)的請求超時,以確保訂單服務(wù)不會無休止地等待客戶服務(wù)的響應(yīng),因為它會使線程永遠(yuǎn)忙碌。這種無休止的等待也會對訂單服務(wù)產(chǎn)生級聯(lián)效應(yīng),并可能耗盡訂單服務(wù)服務(wù)器上的所有可用資源。
按照這個超時邏輯,訂單服務(wù)會向客服請求客戶名稱,如果客服在設(shè)定的時限內(nèi)沒有得到響應(yīng),那么訂單服務(wù)假設(shè)現(xiàn)在沒有機(jī)會得到客戶的成功響應(yīng)服務(wù),因此它終止或超時請求并采取適當(dāng)?shù)男袆硬⒎祷仨憫?yīng)。
要模擬客戶服務(wù)響應(yīng)的延遲,請將以下操作方法添加到客戶服務(wù)中。此方法在 2 分鐘延遲后返回響應(yīng)。為了實(shí)現(xiàn)這種行為,我們使用了 Thread 類中的 sleep 方法,該方法在指定的時間內(nèi)停止線程執(zhí)行。
所以這個客戶服務(wù)操作方法 GetCustomerNameWithDelay 將延遲響應(yīng) 2 分鐘以訂購服務(wù)。
[HttpGet] [Route("GetCustomerNameWithDelay/{customerCode}")] public ActionResult<string> GetCustomerNameWithDelay(int customerCode) {Thread.Sleep(new TimeSpan(0, 2, 0));if (_customerNameDict != null && _customerNameDict.ContainsKey(customerCode)){return _customerNameDict[customerCode];}return "Customer Not Found"; }要在 ASP.NET Core 中使用 Polly 實(shí)現(xiàn)超時邏輯,我們需要聲明 TimeoutPolicy 類型的對象并定義策略,如下面的代碼所示
private static TimeoutPolicy _timeoutPolicy;public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory) {_timeoutPolicy = Policy.Timeout(20, TimeoutStrategy.Pessimistic); }上面的代碼示例將創(chuàng)建一個超時策略,該策略將等待響應(yīng) 20 秒,在 20 秒后,它將假定不可能成功響應(yīng)并且將超時請求,即應(yīng)放棄執(zhí)行委托或函數(shù)。
ASP.NET Core 中 Polly 中的超時策略支持樂觀和悲觀超時。建議盡可能使用樂觀超時,因為它消耗的資源較少。
樂觀 - 假設(shè)您執(zhí)行的委托支持取消,并且委托通過拋出異常來表達(dá)該超時
悲觀 - 認(rèn)識到在某些情況下您可能需要執(zhí)行沒有內(nèi)置超時的委托,并且不接受取消,即調(diào)用者停止等待底層委托完成
接下來,我們將在訂單服務(wù)中添加一個新的操作方法,它將使用 TimeoutPolicy 對象向客戶服務(wù)的新操作方法 (GetCustomerNameWithDelay) 發(fā)出 HTTP 請求,該方法返回延遲響應(yīng)。超時策略用于處理來自客戶服務(wù)的延遲。
[HttpGet] [Route("GetOrderByCustomerWithTimeout/{customerCode}")] public OrderDetails GetOrderByCustomerWithTimeout(int customerCode) {try{_httpClient = _httpClientFactory.CreateClient();_httpClient.BaseAddress = new Uri(apiurl);var uri = "/api/Customer/GetCustomerNameWithDelay/" + customerCode;var result = _timeoutPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);_orderDetails.CustomerName = result;return _orderDetails;}catch(Exception ex){_logger.LogError(ex, "Excpetion Occurred");_orderDetails.CustomerName = "Customer Name Not Available as of Now";return _orderDetails;} }TimeoutPolicy 對象使用委托在 Execute() 委托中對客戶服務(wù)執(zhí)行所需的 HTTP 調(diào)用。如果 HTTP 調(diào)用在 20 秒內(nèi)沒有返回響應(yīng),即根據(jù)超時策略中設(shè)置的時間,則 HTTP 調(diào)用將終止并引發(fā)超時 – operationcancelledexception 和在 catch 塊中,我們已將客戶名稱設(shè)置為 '客戶名稱目前不可用'。這僅用于演示目的,在實(shí)踐中您將向用戶返回錯誤,同時將錯誤通知管理員,以便修復(fù)。
讓我們在 ASP.NET Core 中運(yùn)行和測試 Polly 的超時策略。在 Visual Studio 中運(yùn)行解決方案后,兩個項目(即客戶和訂單)都應(yīng)該啟動。兩個服務(wù)都開始轉(zhuǎn)到訂購服務(wù)后,您應(yīng)該會看到以下來自 swagger (OpenAPI) 的屏幕
在上面的屏幕上選擇操作 /api/Order/GetOrderByCustomerWithTimeout/(customerCode) 它應(yīng)該展開,然后單擊 Try it out 按鈕。之后,您應(yīng)該會看到以下屏幕,您需要在其中輸入客戶代碼的值并單擊執(zhí)行按鈕。
如上所示,點(diǎn)擊執(zhí)行后,根據(jù)我們對超時事件的處理,我們得到了客戶名稱為“客戶名稱截至目前不可用”的成功響應(yīng)。
但是訂單服務(wù)中的 GetOrderByCustomerWithTimeout 操作正在對客戶服務(wù)進(jìn)行 HTTP 調(diào)用,該調(diào)用返回延遲響應(yīng),因此讓我們檢查日志,看看在客戶服務(wù)中對 GetCustomerNameWithDelay 的 HTTP 調(diào)用期間發(fā)生了什么
正如我們在上面的日志截圖中看到的那樣,當(dāng)我們從訂單服務(wù)調(diào)用客戶服務(wù)時,由于訂單服務(wù)超時策略引發(fā)的超時事件導(dǎo)致客戶服務(wù)響應(yīng)延遲,Polly.Timeout.TimeoutRejectedException 類型的異常是引發(fā)并取消操作。在 catch 塊中,我們添加了返回成功的代碼,但具有自定義客戶名稱。因此,通過在訂單服務(wù)中使用超時策略,我們能夠處理客戶服務(wù)的延遲并避免無休止地等待訂單服務(wù)。
回退策略
根據(jù) name 的定義,此策略表明您需要在調(diào)用請求失敗的情況下進(jìn)行一些后備(計劃 B)。現(xiàn)在,在這里您可以首先實(shí)施重試策略以排除正在調(diào)用的服務(wù)的臨時故障,在所有重試服務(wù)也失敗之后,您可以有一些后備機(jī)制,即在失敗的情況下該怎么做。此回退策略允許您在被調(diào)用的服務(wù)失敗時為響應(yīng)提供替代值(或要執(zhí)行的替代操作)。
考慮從訂單服務(wù)到客戶服務(wù)的 HTTP 請求失敗的場景。來自客戶服務(wù)的這個錯誤可能是永久性的,也可能是暫時的。現(xiàn)在即使在重試過程中請求也失敗了,所以不要因為客戶服務(wù)失敗而導(dǎo)致訂單服務(wù)失敗,而是希望為響應(yīng)提供一些替代值,以便訂單服務(wù)可以將其作為響應(yīng)(而不是失敗)和根據(jù)該響應(yīng)執(zhí)行剩余的代碼。
根據(jù)此回退邏輯,訂單服務(wù)將向客戶服務(wù)請求客戶名稱,如果客戶服務(wù)返回異常,則訂單服務(wù)將使用回退策略中配置的替代值作為來自客戶服務(wù)的最終響應(yīng)并處理該響應(yīng)。
要模擬客戶服務(wù)中的永久性故障,請將以下操作方法添加到客戶服務(wù)中。此方法始終返回錯誤。
所以這個客戶服務(wù)操作方法 GetCustomerNameWithPermFailure 將拋出異常并且在所有情況下總是返回錯誤
[HttpGet] [Route("GetCustomerNameWithPermFailure/{customerCode}")] public ActionResult<string> GetCustomerNameWithPermFailure(int customerCode) {try{throw new Exception("Database Not Available");}catch{//Log Errorreturn StatusCode(StatusCodes.Status500InternalServerError);} }要在 ASP.NET Core 中使用 Polly 實(shí)現(xiàn)回退邏輯,我們需要聲明 FallbackPolicy 類型的對象并定義策略,如下面的代碼所示
private readonly FallbackPolicy<string> _fallbackPolicy;public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory) {_fallbackPolicy = Policy<string>.Handle<Exception>().Fallback("Customer Name Not Available - Please retry later"); }上面的代碼示例將創(chuàng)建一個回退策略,如果 HTTP 服務(wù)調(diào)用因策略處理的異常而失敗,則會將響應(yīng)值替換為“客戶名稱不可用 - 請稍后重試”。數(shù)據(jù)類型為字符串 (TResult) 的回退策略被用作客戶服務(wù)操作,返回字符串(客戶名稱)作為響應(yīng)。
您甚至可以將回退策略用于返回?zé)o效的調(diào)用。在 void 的情況下,如果策略處理錯誤(而不是替代返回值),它指定要運(yùn)行的備用操作.Fallback(() => DoSomeFallbackAction())
同樣在上面的代碼中,我們已經(jīng)指定回退策略處理通用異常,因此它將為所有類型的異常提供替代值,但您甚至可以為更具體的異常(如 HttpRequestException)配置回退策略,然后它將僅為HttpRequestException 類型的異常。
接下來,我們將在訂單服務(wù)中添加一個新的操作方法,該方法將使用 FallbackPolicy 對象向返回錯誤的客戶服務(wù)的新操作方法 (GetCustomerNameWithPermFailure) 發(fā)出 HTTP 請求。回退策略用于通過在發(fā)生故障時為響應(yīng)提供回退或替代值來處理客戶服務(wù)失敗。
[HttpGet] [Route("GetOrderByCustomerWithFallback/{customerCode}")] public OrderDetails GetOrderByCustomerWithFallback(int customerCode) {_httpClient = _httpClientFactory.CreateClient();_httpClient.BaseAddress = new Uri(apiurl);var uri = "/api/Customer/GetCustomerNameWithPermFailure/" + customerCode;var result = _fallbackPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);_orderDetails.CustomerName = result;return _orderDetails; }FallbackPolicy 對象使用委托在 Execute() 委托中對客戶服務(wù)執(zhí)行所需的 HTTP 調(diào)用。如果 HTTP 調(diào)用引發(fā)了由回退策略處理的異常,則在失敗時提供替代值。
讓我們在 ASP.NET Core 中運(yùn)行和測試 Polly 的回退策略。在 Visual Studio 中運(yùn)行解決方案后,兩個項目(即客戶和訂單)都應(yīng)該啟動。兩個服務(wù)都開始轉(zhuǎn)到訂購服務(wù)后,您應(yīng)該會看到以下來自 swagger (OpenAPI) 的屏幕
在上面的屏幕上選擇操作 /api/Order/GetOrderByCustomerWithFallback/(customerCode) 它應(yīng)該展開,然后單擊 Try it out 按鈕。之后,您應(yīng)該會看到以下屏幕,您需要在其中輸入客戶代碼的值并單擊執(zhí)行按鈕。
如上所示,點(diǎn)擊執(zhí)行后,我們得到了一個成功的響應(yīng)(即使客戶服務(wù)永久失敗),客戶名稱根據(jù)為客戶代碼配置的后備替代值。
但是訂單服務(wù)中的 GetOrderByCustomerWithFallback 操作正在對客戶服務(wù)進(jìn)行 HTTP 調(diào)用,該調(diào)用返回錯誤,因此讓我們檢查日志,看看在客戶服務(wù)中對 GetCustomerNameWithPermFailure 的 HTTP 調(diào)用期間發(fā)生了什么。
正如我們在上面日志的屏幕截圖中看到的,當(dāng)我們從訂單服務(wù)調(diào)用客戶服務(wù)并返回錯誤但訂單服務(wù)仍然成功并且回退值用于響應(yīng)。因此,通過在訂單服務(wù)中使用回退策略,我們能夠處理客戶服務(wù)中的故障。
斷路器策略
此斷路器策略建議您需要有某種機(jī)制或邏輯來不調(diào)用特定服務(wù),以防該服務(wù)因前幾個請求而永久失敗。當(dāng)服務(wù)請求失敗計數(shù)超過某個預(yù)先配置的閾值時,此斷路器策略允許您配置為在配置的時間段內(nèi)阻止對特定失敗服務(wù)的 HTTP 請求。
考慮從訂單服務(wù)到客戶服務(wù)的 HTTP 請求失敗的場景。來自客戶服務(wù)的這個錯誤可能是永久性的,也可能是暫時的。現(xiàn)在即使在重試期間請求也失敗了,因此您使用回退策略為響應(yīng)提供了一些替代值。但是現(xiàn)在由于很少有連續(xù)呼叫客戶服務(wù)失敗,所以一段時間(比如幾分鐘)你不想浪費(fèi)時間打電話給客戶服務(wù)而是假設(shè)它會返回一個錯誤并使用備用響應(yīng)來處理請求訂購服務(wù)。
此邏輯假設(shè),如果服務(wù)連續(xù)失敗幾次,則該服務(wù)存在一些永久性問題,可能需要一些時間來糾正問題。因此,讓我們不要浪費(fèi)時間調(diào)用或重試失敗的服務(wù),而是采取備用回退路徑為服務(wù)提供一些時間來恢復(fù)。
按照這個斷路器邏輯,訂單服務(wù)會向客服請求客戶姓名,如果客服連續(xù)2次返回異常,則電路將斷開(即電路將打開)1分鐘,并持續(xù)1分鐘訂單服務(wù)不會打電話給客戶服務(wù),而是自己假設(shè)客戶服務(wù)會返回錯誤。
為了模擬客戶服務(wù)的永久性故障,我們將使用客戶服務(wù)中的 GetCustomerNameWithPermFailure 操作,我們用于演示回退策略。
要在 ASP.NET Core 中使用 Polly 實(shí)現(xiàn)斷路器邏輯,我們需要聲明 CircuitBreakerPolicy 類型的對象并定義策略,如下面的代碼所示
private static CircuitBreakerPolicy _circuitBreakerPolicy;public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory) {if (_circuitBreakerPolicy == null){_circuitBreakerPolicy = Policy.Handle<Exception>().CircuitBreaker(2, TimeSpan.FromMinutes(1));} }上面的代碼示例將創(chuàng)建一個斷路器策略,該策略定義在調(diào)用服務(wù)時如果連續(xù) 2 次出現(xiàn)異常,則電路將中斷(對服務(wù)的調(diào)用將被阻止)2 分鐘的時間跨度。
同樣在上面的代碼中,我們已經(jīng)指定斷路器策略處理通用異常,因此它會因所有類型的異常而中斷,但您甚至可以為更具體的異常(如 HttpRequestException)配置斷路器策略,然后它只會因以下異常而中斷類型 HttpRequestException。
接下來,我們將在訂單服務(wù)中添加一個新的操作方法,它將利用斷路器策略對象向客戶服務(wù)的操作方法 (GetCustomerNameWithPermFailure) 發(fā)出 HTTP 請求,該方法返回錯誤。斷路器策略用于處理客戶服務(wù)的故障,在客戶服務(wù)連續(xù) 2 次失敗后 1 分鐘內(nèi)不撥打任何電話。
[HttpGet] [Route("GetOrderByCustomerWithCircuitBreaker/{customerCode}")] public OrderDetails GetOrderByCustomerWithCircuitBreaker(int customerCode) {try{_httpClient = _httpClientFactory.CreateClient();_httpClient.BaseAddress = new Uri(apiurl);var uri = "/api/Customer/GetCustomerNameWithPermFailure/" + customerCode;var result = _circuitBreakerPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);_orderDetails.CustomerName = result;return _orderDetails;}catch (Exception ex){_logger.LogError(ex, "Excpetion Occurred");_orderDetails.CustomerName = "Customer Name Not Available as of Now";return _orderDetails;} }斷路器策略對象使用委托在 Execute() 委托中執(zhí)行所需的對客戶服務(wù)的 HTTP 調(diào)用。如果 HTTP 調(diào)用引發(fā)異常,該異常正在由 catch 塊處理以提供客戶名稱的替代值。
讓我們在 ASP.NET Core 中運(yùn)行和測試 Polly 的斷路器策略。在 Visual Studio 中運(yùn)行解決方案后,兩個項目(即客戶和訂單)都應(yīng)該啟動。兩個服務(wù)都開始轉(zhuǎn)到訂購服務(wù)后,您應(yīng)該會看到以下來自 swagger (OpenAPI) 的屏幕
在上面的屏幕上選擇操作 /api/Order/GetOrderByCustomerWithCircuitBreaker/(customerCode) 它應(yīng)該展開,然后單擊 Try it out 按鈕。之后,您應(yīng)該會看到以下屏幕,您需要在其中輸入客戶代碼的值并單擊執(zhí)行按鈕。
如上所示,點(diǎn)擊執(zhí)行后,我們得到了一個成功的響應(yīng)(即使客戶服務(wù)永久失敗),客戶名稱與 catch 塊中配置的回退值一致。
但是訂單服務(wù)中的 GetOrderByCustomerWithCircuitBreaker 操作正在對客戶服務(wù)進(jìn)行 HTTP 調(diào)用,該調(diào)用返回錯誤,因此讓我們檢查日志,看看在客戶服務(wù)中對 GetCustomerNameWithPermFailure 進(jìn)行 HTTP 調(diào)用期間發(fā)生了什么
Polly 與斷路器策略正如我們在上面日志的屏幕截圖中看到的那樣,當(dāng)我們從訂單服務(wù)調(diào)用客戶服務(wù)時,它返回了一個錯誤,訂單服務(wù)使用了來自 catch 塊的客戶名稱的替代值。此外,我們可以從日志中看到,當(dāng)我們在電路打開時嘗試致電客戶服務(wù)時,Polly 沒有致電客戶服務(wù),而是為該 Policy.CircuitBreaker.BrokenCircuitException 提供了一個異常 – 電路現(xiàn)在已打開并且不允許通話。
Bulkhead策略
要在 ASP.NET Core 中使用 Polly 實(shí)現(xiàn)艙壁隔離邏輯,我們需要聲明 BulkheadPolicy 類型的對象并定義策略,如下面的代碼所示
private static BulkheadPolicy _bulkheadPolicy;public OrderController(ILogger<OrderController> logger, IHttpClientFactory httpClientFactory) {_bulkheadPolicy = Policy.Bulkhead(3, 6); }上面的代碼示例將創(chuàng)建一個Bulkhead策略,該策略定義在調(diào)用服務(wù)時限制調(diào)用服務(wù)的資源數(shù)量,即通過隔板的最多 3 個并行執(zhí)行和最多 6 個可能正在排隊的請求數(shù)(等待獲取執(zhí)行槽)隨時。
接下來,我們將在 order 服務(wù)中添加一個新的 action 方法,它將使用 Bulkhead Isolation Policy 對象向客戶服務(wù)的 action 方法 (GetCustomerName) 發(fā)出 HTTP 請求。隔板隔離策略用于限制用于調(diào)用客戶服務(wù)的資源,即在任何給定時間將有 3 個并行請求執(zhí)行,另外 6 個請求可以在隊列中。這樣如果客戶服務(wù)的響應(yīng)被延遲或阻塞,那么我們不會在訂單服務(wù)上使用太多資源,也會導(dǎo)致訂單服務(wù)發(fā)生級聯(lián)故障。
[HttpGet] [Route("GetOrderByCustomerWithBulkHead/{customerCode}")] public OrderDetails GetOrderByCustomerWithBulkHead(int customerCode) {_httpClient = _httpClientFactory.CreateClient();_httpClient.BaseAddress = new Uri(apiurl);var uri = "/api/Customer/GetCustomerName/" + customerCode;var result = _bulkheadPolicy.Execute(() => _httpClient.GetStringAsync(uri).Result);_orderDetails.CustomerName = result;return _orderDetails; }隔板隔離策略對象使用委托在 Execute() 委托中執(zhí)行所需的對客戶服務(wù)的 HTTP 調(diào)用。
隔板隔離策略適用于一個錯誤不應(yīng)導(dǎo)致整艘船癱瘓的策略!即當(dāng)服務(wù)開始失敗時,它會建立大量請求,這些請求都在并行緩慢失敗,這可能導(dǎo)致訂單服務(wù)中資源(CPU/線程/內(nèi)存)的使用,從而降低能力或?qū)е路?wù)失敗訂購服務(wù)。
對于緩存策略,我建議不要實(shí)現(xiàn)基于異常緩存數(shù)據(jù)的邏輯,而是根據(jù)數(shù)據(jù)設(shè)計緩存邏輯,即靜態(tài)/動態(tài)數(shù)據(jù)、常用數(shù)據(jù)等。您可以閱讀我關(guān)于在 ASP 中緩存的詳細(xì)文章。NET Core在這里[4]
到目前為止,我們已經(jīng)了解了 ASP.NET Core 中 Polly 的重要策略。此外,可以在 ASP.NET Core 中為單個服務(wù)調(diào)用組合 Polly 的多個策略,例如
fallback.Wrap(waitAndRetry).Wrap(breaker).Execute(action);fallback.Execute(() => waitAndRetry.Execute(() =>breaker.Execute(action)));概括
我們在 ASP.NET Core Web API 中了解了 Polly 的各種策略。我們在本文中看到的只是冰山一角,即我們剛剛開始并查看了策略的基本實(shí)施。
您甚至可以嘗試組合多個策略,稱為策略包裝,您可以將重試策略與回退策略或斷路器策略相結(jié)合。
我們看到了 ASP.NET Core 中 Polly 的實(shí)現(xiàn)以及策略的同步方法,所有策略也存在類似的異步方法。
下載源代碼
在這里您可以下載作為本文一部分開發(fā)的完整源代碼,即 ASP.NET Core Web API 中的 Polly
References
[1]?使用 ASP.NET Core:?https://procodeguide.com/programming/microservices-asp-net-core/
[2]?Polly:?https://github.com/App-vNext/Polly
[3]?此處:?https://procodeguide.com/programming/aspnet-core-logging-with-serilog/
[4]?在這里:?https://procodeguide.com/programming/aspnet-core-caching/
總結(jié)
以上是生活随笔為你收集整理的在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不止命令行!自定义VS生成事件
- 下一篇: Gitflow branch与Docke