共计 2372 个字符,预计需要花费 6 分钟才能阅读完成。
一、为什么要设置超时与重试
平时开发过程中,可能很多人都不会意识到设置超时的重要性。如果利用不设置超时,调用的服务接口可能会因为各种起因(异样、连接池 / 线程池耗尽、SQL 慢查问等)导致响应慢,一旦慢申请累积地越来越多,导致的结果就是连锁反应,即咱们常说的雪崩效应。超时后,咱们能够抉择重试几次。读服务重试没有问题,但写服务重试可能会造成反复,如下订单,除非业务方做了幂等解决。重试的频率和次数如果过多,也会造成申请流量过大,对系统造成冲击。因而必须依据理论业务场景设置正当的超时重试机制,必要时应该熔断疾速失败,而不是一直重试。
二、超时的链路档次
通常咱们的利用会像下图的构造,申请开始到返回响应会通过一条残缺的链路:浏览器 /app 客户端 -> 代理层(负载平衡) -> Web 容器层(Tomcat 等) -> 业务逻辑层(Future 等) -> 中间件层(Redis 等) -> 数据库层(Mysql 等)。每一层都能够设置超时与重试机制。上面开展细说。
1. 代理层超时
这里以 nginx 为例。nginx 次要有客户端超时、DNS 解析超时、代理超时。
①客户端超时
client_header_timeout time: 设置读取客户端申请头超时工夫,默认 60s。
client_body_timeout time: 设置读取客户端内容体超时工夫,默认 60s。
send_timeout time: 设置发送响应到客户端的超时工夫,默认 60s。
keepalive_timeout timeout [header_timeout]:设置 HTTP 长连贯超时工夫。第一个参数 timeout 是通知 nginx 长连贯超时工夫是多少,默认 75s,第二个参数 header_timeout 用于设置响应头“Keep-Alive: timeout=time”,即告知客户端长连贯超时工夫。
②DNS 解析超时
resolver_timeout time:设置 DNS 解析超时工夫,默认 30s。
③代理超时
proxy_connect_timeout time: 与后端 / 上游服务器建设连贯的超时工夫,默认 60s。
proxy_read_timeout time: 设置从后端 / 上游服务器读取响应的超时工夫,默认 60s。
proxy_send_timeout time: 设置往后端 / 上游服务器发送申请的超时工夫,默认 60s。
nginx 除了超时参数设置,也有重试参数设置。
proxy_next_upstream error|timeout|invalid_header|http_500|http_502|http_503
配置什么状况下须要申请下一台上游服务器进行重试。
proxy_next_upstream_tries number:设置重试次数,默认 0 示意不限度。
proxy_next_upstream_timeout time:设置重试最大超时工夫,默认 0 示意不限度。
max_fails 和 fail_timeout:配置什么时候 nginx 将上游服务器认定为不可用 / 不存活。当上游服务器在 fail_timeout 工夫内失败了 max_fails 次,则认为该上游服务器不可用,并在接下来的 fail_timeout 工夫内从 upstream 摘除该节点。
2.Web 容器超时
以 Tomcat8.5 版本为例。
connectionTimeout: 配置与客户端建设连贯的超时工夫,默认 60s
socket.soTimeout: 从客户端读取申请数据的超时工夫,默认 60s
asyncTimeout:servlet3 异步申请的超时工夫,默认 30s
keepAliveTimeout 和 maxKeepAliveRequests: 和 nginx 长连贯相似,keepAliveTimeout 默认为 - 1 示意永不超时,maxKeepAliveRequests 默认为 100
3. 业务超时
在多线程编程中,咱们常常会应用 Future 接口,来异步接管线程的执行后果,咱们能够设置超时工夫,而不是始终阻塞上来,即应用Future.get(timeout, TimeUnit)。
4. 中间件超时
以 Dubbo 为例。
<dubbo:consumer timeout="1000"></dubbo:consumer>
<dubbo:provider timeout="1000"></dubbo:provider>
其实像 zookeeper,redis,kafka 等中间件都有相应的超时设置,感兴趣地能够去翻下官网文档。
5. 数据库超时
以 dpcp 连接池为例
<bean id=”dataSource” class=”org.apache.commons.dbcp2.BaseDataSource” destroy-method=”close”>
<!– Statement 默认超时工夫 –>
<property name=”defaultQueryTimeout” value=”3″ />
<!– socket 连贯 / 读超时 –>
<property name=”connectionProperties” value=”connectTimeout=2000; socketTimeout=2000″ />
<!– 期待获取连接池连贯最大超时工夫 –>
<property name=”maxWaitMillis” value=”500″ />
</bean>
三、总结
本文介绍了超时的重要性。超时后的策略能够是重试(等一会再试,重试其余分组服务,重试其余机房服务等)、摘掉不可用节点,返回托底数据和页面等等。
对于非幂等的写服务应该防止重试,或者能够思考提前生成惟一流水号来保障写服务操作的幂等性。
超时重试尽管进步了可用性,但也必然会导致 RT 即响应工夫减少,重试次数过多可能会影响用户体验,因而在理论开发中要依据业务场景思考用户的容忍最长等待时间。
参考资料
《亿级流量网站架构核心技术》