在实践中重试HTTP标头
Retry-After是鮮為人知的HTTP響應標頭。 讓我引用RFC 2616(HTTP 1.1規范)的相關部分:
14.37重試后
Retry-After響應標頭字段可與503 ( 服務不可用 )響應一起使用,以指示請求該客戶端的服務預計無法使用多長時間。 該字段也可以與任何3xx(重定向)響應一起使用,以指示在發出重定向請求之前,要求用戶代理等待的最短時間。 該字段的值可以是響應日期之后的HTTP日期或整數秒(十進制)。
Retry-After = "Retry-After" ":" ( HTTP-date | delta-seconds )其用法的兩個示例是:
Retry-After: Fri, 31 Dec 1999 23:59:59 GMT Retry-After: 120在后一個示例中,延遲為2分鐘。
盡管具有3xx響應的用例很有趣,尤其是在最終一致的系統中(“ 您的資源將在2秒內在此鏈接下可用 ),但我們將重點放在錯誤處理上。 通過將Retry-After添加到響應服務器,服務器可以在客戶端再次可用時提供提示。 有人可能會爭辯說,服務器幾乎不知道何時將其重新聯機,但是在幾種有效的用例中,可以通過某種方式推斷出這種知識:
- 計劃的維護–這很明顯,如果您的服務器在計劃的維護窗口內關閉,則可以從代理發送Retry-After ,并提供準確的回叫時間。 如果客戶理解并尊重此標頭,客戶當然不會更早地重試
- 隊列/線程池已滿-如果您的請求必須由線程池處理且已滿,則可以估計何時可以處理下一個請求。 這需要綁定隊列(請參閱: ExecutorService – 10個技巧和竅門 ,第6點),并粗略估計處理一項任務需要多長時間。 有了這些知識,您可以估計何時可以在不排隊的情況下為下一個客戶提供服務。
- 斷路器打開–在Hystrix中,您可以查詢
- 下一個可用令牌/資源/任何
讓我們關注一個非平凡的用例。 假設您的Web服務由Hystrix命令支持:
private static final HystrixCommand.Setter CMD_KEY = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("REST")).andCommandKey(HystrixCommandKey.Factory.asKey("fetch"));@RequestMapping(value = "/", method = GET) public String fetch() {return fetchCommand().execute(); }private HystrixCommand<String> fetchCommand() {return new HystrixCommand<String>(CMD_KEY) {@Overrideprotected String run() throws Exception {//...}}; }這可以按預期工作,如果命令失敗,超時或斷路器打開,客戶端將收到503。但是,在斷路器的情況下,我們至少可以估計重新閉合電路需要多長時間。 不幸的是,沒有公共API可以告訴您在發生災難性故障時確切的電路將保持斷開狀態多長時間。 但是我們知道斷路器默認保持斷開狀態多長時間,這是一個很好的最大估計值。 當然,如果基礎命令不斷失敗,電路可能會保持斷開狀態。 但是Retry-After不能保證服務器會在給定的時間運行,這只是客戶端停止嘗試的提示。 以下實現很簡單,但是很糟糕:
@RequestMapping(value = "/", method = GET) public ResponseEntity<String> fetch() {final HystrixCommand<String> command = fetchCommand();if (command.isCircuitBreakerOpen()) {return handleOpenCircuit(command);}return new ResponseEntity<>(command.execute(), HttpStatus.OK); }private ResponseEntity<String> handleOpenCircuit(HystrixCommand<String> command) {final HttpHeaders headers = new HttpHeaders();final Integer retryAfterMillis = command.getProperties().circuitBreakerSleepWindowInMilliseconds().get();headers.set(HttpHeaders.RETRY_AFTER, Integer.toString(retryAfterMillis / 1000));return new ResponseEntity<>(headers, HttpStatus.SERVICE_UNAVAILABLE); }如您所見,我們可以詢問任何命令其斷路器是否斷開。 如果打開,則使用circuitBreakerSleepWindowInMilliseconds值設置Retry-After標頭。 該解決方案存在一個細微但災難性的錯誤:如果某一天電路斷開,我們將再也不會運行命令,因為我們會急于返回503。這意味著Hystrix將永遠不會重試執行它,并且電路將永遠保持斷開狀態。 我們必須嘗試每次調用命令并捕獲適當的異常:
@RequestMapping(value = "/", method = GET) public ResponseEntity<String> fetch() {final HystrixCommand<String> command = fetchCommand();try {return new ResponseEntity<>(command.execute(), OK);} catch (HystrixRuntimeException e) {log.warn("Error", e);return handleHystrixException(command);} }private ResponseEntity<String> handleHystrixException(HystrixCommand<String> command) {final HttpHeaders headers = new HttpHeaders();if (command.isCircuitBreakerOpen()) {final Integer retryAfterMillis = command.getProperties().circuitBreakerSleepWindowInMilliseconds().get();headers.set(HttpHeaders.RETRY_AFTER, Integer.toString(retryAfterMillis / 1000));}return new ResponseEntity<>(headers, SERVICE_UNAVAILABLE); }這個很好用。 如果命令拋出異常并且相關電路斷開,我們將設置適當的標題。 在所有示例中,我們花費毫秒并歸一化為秒。 我不推薦這樣做,但是如果由于某些原因,您更喜歡Retry-After標頭中的絕對日期而不是相對超時,則HTTP日期格式最終是Java的一部分(自JDK 8起):
import java.time.format.DateTimeFormatter;//...final ZonedDateTime after5seconds = ZonedDateTime.now().plusSeconds(5); final String httpDate = DateTimeFormatter.RFC_1123_DATE_TIME.format(after5seconds);關于自動DDoS的注意事項
如果將相同的時間戳發送給許多唯一的客戶端,則必須謹慎使用Retry-After標頭。 想象現在是15:30,然后您將Retry-After: Thu, 10 Feb 2015 15:40:00 GMT發送給周圍的所有人-只是因為您以某種方式估計服務將在15:40開通。 您持續發送相同時間戳的時間越長,尊重Retry-After客戶端所期望的DDoS“攻擊”就越大。 基本上每個人都會在15:40安排重試的時間(很明顯,時鐘未完全對齊,并且網絡延遲有所變化,但仍然存在),從而使系統充滿了請求。 如果您的系統設計正確,那么您可能會幸免于難。 但是,您可能會通過發送另一個固定的Retry-After標頭來緩解這種“攻擊”,此舉實際上是Retry-After重新安排攻擊時間。
話雖這么說,避免將固定的絕對時間戳發送給多個唯一的客戶端。 即使您確切知道系統何時可用,也可以Retry-After一段時間內分散“ Retry-After值。 實際上,您應該逐漸讓越來越多的客戶進入,因此請嘗試不同的概率分布。
摘要
Retry-After HTTP響應標頭既不是普遍已知的,也不是經常適用的。 但是在極少數情況下可以預期停機的情況下,請考慮在服務器端實施停機。 如果客戶端也意識到這一點,則可以在減少系統流量和改善系統吞吐量和響應時間的同時,大幅度減少網絡流量。
翻譯自: https://www.javacodegeeks.com/2015/02/retry-http-header-practice.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的在实践中重试HTTP标头的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 人死后几天圆坟(人死后为啥圆坟?怎样圆坟
- 下一篇: qq电脑管家文件粉碎(怎么用腾讯电脑管家