在某些情況下,WEB API 可能需要很長時間來處理請求,而客戶端如果一直等待工作完成是不可行的,比如連接超時等。
這時,可以使用“異步 Request-Reply 模式”。
異步 Request-Reply 模式
異步 Request-Reply 模式是指:在后端處理需要是異步處理但前端仍需要明確響應的情況下,將后端處理與前端分離。
整體流程如下:
客戶端應用程序對業務 API 進行調用,在后端觸發長時間運行的操作;
API 立即返回響應。返回 HTTP 202 Accepted (接受) 狀態碼,確認已收到請求進行處理,響應包含一個標頭,包含了客戶端可以輪詢狀態的 API 地址,以檢查長時間運行的操作的結果;;
客戶端輪詢這個狀態 API,如果操作未完成,則返回 HTTP 202,否則返回 HTTP 200, 并包含實際的響應數據。
下面我們來演示如何在 ASP.NET Core 中實現。
Demo
我們用等待20秒模擬一個長時間 API 操作:
[HttpGet]
[Route("get")]
public?async?Task<IEnumerable<WeatherForecast>>?Get()
{await?Task.Delay(20000);var?rng?=?new?Random();return?Enumerable.Range(1,?5).Select(index?=>?new?WeatherForecast{Date?=?DateTime.Now.AddDays(index),TemperatureC?=?rng.Next(-20,?55),Summary?=?Summaries[rng.Next(Summaries.Length)]}).ToArray();
}
首先,我們創建一個新 API 操作:
[HttpGet]
[Route("async/get")]
public?async?Task<IActionResult>?AsyncGet()
{string?id?=?Guid.NewGuid().ToString();string?responseValue?=?$@"/status/{id}";_cache.SetString(id,?responseValue);Task.Factory.StartNew(()?=>{var?result?=?Get().Result;_cache.SetString(id?+?"_result",?JsonConvert.SerializeObject(result));});return?Accepted(responseValue);
}
具體作用就是把長時間交由 Task 執行,執行結果將放到分布式緩存中,然后立刻返回 HTTP 202 Accepted。
客戶端不再訪問原來的請求地址,而是使用此新地址:
然后,我們創建一個狀態 API 用于輪詢:
[HttpGet]
[Route("/status/{id}")]
public?IActionResult?Status(string?id)
{var?result?=?_cache.GetString(id?+?"_result");if?(!string.IsNullOrEmpty(result)){return?Ok(result);}return?Accepted(_cache.GetString(id));
}
客戶端根據 ID 進行輪詢。
當操作還未完成時,繼續返回 HTTP 202:
當操作完成時,返回分布式緩存中的結果:
結論
如果你的 API 有長時間運行的操作,應將輪詢信息盡快地返回給調用方,以便他們可以檢查進度。
想了解更多內容,請關注我的個人公眾號”My IO“