在实践中重试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://www.mzph.cn/news/360886.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java中连接字符串的最佳方法

最近有人问我这个问题–在Java中使用运算符连接字符串是否对性能不利&#xff1f; 这让我开始思考Java中连接字符串的不同方法&#xff0c;以及它们如何相互对抗。 这些是我要研究的方法&#xff1a; 使用运算符 使用StringBuilder 使用StringBuffer 使用String.concat() …

时间转换以及公式

moment(item.updatedAt).fromNow() ;//距离今天多久了moment&#xff08;&#xff09;获得今天、明天和昨天的日期 let today moment(new Date());let tomorrow moment(new Date()).add(1,days);let yesterday moment(new Date()).add(-1, days);后续遇到继续补充

只不过是R.java文件的特性-----出错信息:R.java was modified manually! Reverting to generated version!...

出错信息:R.java was modified manually! Reverting to generated version! 出错原因&#xff1a;今天在res下建立了一个drawable的文件夹存放图片资源----图片名为1.jpg 后来R.java文件就报错了&#xff1a;R.java was modified manually! Reverting to generated version! 看…

十大最常见的Java性能问题

Java性能是所有Java应用程序开发人员都关心的问题&#xff0c;因为快速使应用程序与使其正常运行同等重要。 史蒂文海恩斯&#xff08;Steven Haines&#xff09;使用他在Java性能问题上的个人经验得出的结论是&#xff0c; 大多数问题都有共同的根本原因 。 因此&#xff0c;作…

antd form 初始化时间

initialValue: moment(record.showTime, YYYY-MM-DD HH:mm:ss)

uni-app 组件中的canvas转化为图片报错:errMsg:“canvasToTempFilePath:fail canvas is empty”

项目场景&#xff1a; uni-app,开发微信小程序 使用&#xff1a; wx.canvasToTempFilePath({canvasId: line,success: function(res) {console.log(canvasToImg, res);this.radarImg res.tempFilePath;},fail: function(res) {console.log(res);}});问题描述&#xff1a; ec…

正确获取Java事件通知

实现观察者模式以提供Java事件通知似乎是一件容易的事。 但是&#xff0c;容易陷入一些陷阱。 这是我在各种场合不慎造成的常见错误的解释…… Java事件通知 让我们从一个简单的bean StateHolder开始&#xff0c;它封装了带有适当访问器的私有int字段state &#xff1a; publ…

使用fn函数控制页面显示内容

在使用&#xff25;&#xff2c;的时候&#xff0c;不可避免的遇到&#xff0c;截取字符串&#xff0c;判断字符串长度等情况。这里给出简单的通过&#xff46;&#xff4e;函数操作字符串的deamon。 1、页面引入标签 <% taglib prefix"c" uri"http://java.s…

uni-app微信获取手机号,第一次解密总是失败

项目场景&#xff1a; uni-app; 获取code&#xff0c;后台解密手机号 问题描述&#xff1a; 每次第一次登陆&#xff0c;后台都会解密失败 原因分析&#xff1a; code获取错误&#xff1b;导致后台的解密key与code不对应 解决方案&#xff1a; 小程序获取手机号之前&#xf…

Java中不一致的操作会扩大规则

总览 当您在Java中执行一元或二进制操作时&#xff0c;标准行为是使用最宽的操作数&#xff08;或对byte &#xff0c; short和char使用更宽的操作数&#xff09;。 这很容易理解&#xff0c;但是如果考虑最佳类型可能会造成混淆。 乘法 当执行乘法运算时&#xff0c;您得到的…

圣诞节到了,用js给喜欢的人写一颗圣诞树吧

文章目录 1、效果预览2、代码2.1、定义数组写下祝福语2.2、模拟雪花落下的效果2.3、设置背景粒子2.4、操作动画效果2.5、定义闪烁效果2.6、定义粒子对象2.7、粒子对象播放2.8、绘制星星2.9、绘制圣诞树2.10、绘制星星背景动画2.11、定义初始化函数并调用 3、结尾 1、效果预览 圣…

Unity3D 访问Access数据库

Unity3D 访问Access数据库 在开始这个小教程之前呢&#xff0c;其实在网上你已经可以找到相关的资料了&#xff0c;但是我还是要把我自己做练习的一点东西分享出来。写这个教程的主要原因呢&#xff0c;是一个朋友在u3d的官网论坛里&#xff0c;找到了这个demo&#xff0c;但是…

uni.reLaunch前出现uni.showToast,不会成功弹出提示信息

解决方案&#xff1a; uni.showToast({title: 发布成功,duration: 1000});setTimeout(function() {uni.reLaunch({url: /pages/tips/index})}, 1000);

LaTeX 基础笔记。开篇

LaTeX 的起源非常牛逼&#xff0c;有一套书大家可能听说过《计算机程序设计艺术》&#xff0c;写了好几本。当然能在计算机方面写上艺术俩字的书恐怕不是我们一般人能读懂得东西了。他的作者在1976年准备写第二卷的时候发现计算机的排版非常难看&#xff0c;所以&#xff0c;为…

Java旧版不断发展

我最近偶然发现了JDK API的一个非常有趣的警告&#xff0c;即Class.getConstructors()方法。 它的方法签名是这样的&#xff1a; Constructor<?>[] getConstructors()有趣的是&#xff0c; Class.getConstructor(Class...)返回一个Constructor<T> &#xff0c;并…

React 学习笔记 —— Ref Hook

用以下三种方式创建 Ref 都可以 import React from reactexport default function Count () {const [count ,setCount] React.useState(0)const myRef React.createRef()const myRef2 React.useRef() // Ref Hook 的方式const myRef3 {current: undefined}const addNumber…

MFC消息机制

MFC消息机制 MFC消息机制涉及许多知识&#xff0c;比如消息分类&#xff0c;消息映射等。知识先了解一下&#xff0c;马上动手实践才是硬道理。我建了个SDI项目&#xff0c;把常用的消息试验了一遍。如果像我一样初学的&#xff0c;可以留下邮箱索取源码。// MainFrm.h afx_msg…

带Lambda表达式的Apache Wicket

这是怎么回事&#xff1f; :) 我一直在从事一些项目&#xff0c;这些项目值得庆幸的是将Apache Wicket用于表示层。 我自然想到Java的8个lambda表达式如何与Wicket完美匹配。 而不仅仅是我&#xff0c; Wicket团队似乎已经在努力更改API&#xff0c;以为开箱即用的lambda提供支…

React 父组件和子组件中的方法相互调用

目录父组件调用子组件方法子组件调用父组件方法父组件调用子组件方法 父组件中调用子组件的getTree方法 父组件 setFormValue()>{this.TreeList.getTree}<TreeList onSelect{this.setFormValue} onRef{(ref) > { this.TreeList ref }} />子组件 componentDidMount…