亿级流量网站架构核心技术(超时与重试机制)

代理层超时与重试

如果应用不设置超时,则可能会导致请求响应慢,慢请求累积导致连锁反应,甚至造成应用雪崩。而有些中间件或者框架在超时后会进行重试(如设置超时重试两次),读服务天然适合重试,但写服务大多不能重试(如写订单,如果写服务是幂等的,则重试是允许的),重试次数太多会导致多倍请求流量,即模拟了DDoS攻击,后果可能是灾难,因此,务必设置合理的重试机制,并且应该和熔断、快速失败机制配合。

Nginx相关超时设置

Nginx主要有4类超时设置:客户端超时设置、DNS解析超时设置、代理超时设置,如果使用ngx_lua,则还有Lua相关的超时设置。

客户端超时设置

对于客户端超时主要设置有读取请求头超时时间、读取请求体超时时间、发送响应超时时间、长连接超时时间。

  • client_header_timeout time: 设置读取客户端请求头超时时间,默认为60s,如果在此超时时间内客户端没有发送完请求头,则响应408(Request Time-out)状态码给客户端。
  • client_body_timeout time: 设置读取客户端内容体超时时间,默认为60s,此超时时间指的是两次成功读操作间隔时间,而不是发送整个请求体的超时时间,如果在此超时时间内客户端没有发送任何请求体,则响应408(Request Time-out)状态码给客户端。
  • send_timeout time: 设置发送响应到客户端的超时时间,默认为60s,此超时时间指的也是两次成功写操作间隔时间,而不是发送整个响应的超时时间。如果在此超时时间内客户端没有接收任何响应,则Nginx关闭此连接。
  • keepalive_timeout timeout [header_timeout]: 设置HTTP长连接超时时间。其中,第一个参数timeout是告诉Nginx长连接超时时间是多少,默认为75s。第二个参数header_timeout用于设置响应头“Keep-Alive: timeout=time”,即告知客户端长连接超时时间。两个参数可以不一样,“Keep-Alive: timeout=time”响应头可以在Mozilla和Konqueror系列浏览器中起作用,而MSIE长连接默认大约为60s,而不会使用“Keep-Alive: timeout=time”。如果timeout设置为0,则表示禁用长连接。

此参数要配合keepalive_disable 和keepalive_requests一起使用。keepalive_disable表示禁用哪些浏览器的长连接,默认值为msie6,即禁用一些老版本的MSIE的长连接支持。keepalive_requests参数的作用是一个客户端可以通过此长连接的请求次数,默认为100。
keepalive_timeout和keepalive_requests是控制长连接的两个维度,只要其中一个到达设置的阈值,连接就会被关闭。

代理超时设置
在这里插入图片描述

  • proxy_connect_timeout time: 与后端/上游服务器建立连接的超时时间,默认为60s,此时间不超过75s。

  • proxy_read_timeout time: 设置从后端/上游服务器读取响应的超时时间,默认为60s,此超时时间指的是两次成功读操作间隔时间,而不是读取整个响应体的超时时间,如果在此超时时间内上游服务器没有发送任何响应,则Nginx关闭此连接。

  • proxy_send_timeout time: 设置往后端/上游服务器发送请求的超时时间,默认为60s,此超时时间指的是两次成功写操作间隔时间,而不是发送整个请求的超时时间,如果在此超时时间内上游服务器没有接收任何响应,则Nginx关闭此连接。

失败重试机制设置

  • proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | ht tp_503 | http_504 |http_403 | http_404 | non_idempotent | off …:配置什么情况下需要请求下一台上游服务器进行重试。
  • proxy_next_upstream_tries number: 设置重试次数,默认0表示不限制。
  • proxy_next_upstream_timeout time: 设置重试最大超时时间,默认0表示不限制。

upstream存活超时设置

  • max_fails 和fail_timeout: 配置什么时候Nginx将上游服务器认定为不可用/不存活。
    当上游服务器在fail_timeout时间内失败了max_fails次,则认为该上游服务器不可用/不存活。并在接下来的fail_timeout时间内从upstream摘掉该节点(即请求不会转发到该上游服务器)。

ngx_lua超时设置

当我们使用ngx_lua时,也应考虑设置如下网络连接/读/写超时。

lua_socket_connect_timeout 100ms;
lua_socket_send_timeout  200ms;
lua_socket_read_timeout  500ms;

在使用Lua时,我们会按照如下策略进行重试。
在这里插入图片描述
即如果状态码是500/502/503/504,并且该次请求耗时在200ms以内,则我们进行一次重试。

Twemproxy

Twemproxy是Twitter开源的Redis和Memcache代理中间件,其目的是减少与后端缓存服务器的连接数。

timeout: 表示与后端服务器建立连接、接收响应的超时时间,默认永不超时。

server_retry_timeout 和server_failure_limi t: 当开启auto_eject_hosts,即当后端服务器不可用时自动摘除这些节点并在一定时间后进行重试。server_failure_limit设置连续失败多少次后将节点临时摘除,server_retry_timeout设置摘除节点后等待多久进行重试,从而保证不永久性地将节点摘除。

业务超时
  • 任务型 :比如,订单超时未支付取消超时活动自动关闭等,这属于任务型超时,可以通过Worker定期扫描数据库修改状态。有时需要调用的远程服务超时了(比如,用户注册成功后,需要给用户发放优惠券),可以考虑使用队列或者暂时记录到本地稍后重试。

  • 服务调用型 :比如,某个服务的全局超时时间为500ms,但我们有多处服务调用,每处服务调用的超时时间可能不一样,此时,可以简单地使用Future来解决问题,通过如Future.get(3000, TimeUnit.MILLISECONDS)来设置超时。

总结

客户端和服务器端都应该设置超时时间,而且客户端根据场景可以设置比服务器端更长的超时时间。如果存在多级依赖关系,如A调用B,B调用C,则超时设置应该是A>B>C,否则可能会一直重试,引起DDoS攻击效果。不过最终如何选择还是要看场景,有时候客户端设置的超时时间就是要比服务器端的短,可以通过在服务器端实施限流/降级等手段防止DDoS攻击。

超时之后应该有相应的策略来处理,常见的策略有重试(等一会儿再试、尝试其他分组服务、尝试其他机房服务,重试算法可考虑使用如指数退避算法)、摘掉不存活节点(负载均衡/分布式缓存场景下)、托底(返回历史数据/静态数据/缓存数据)、等待页或者错误页。

对于非幂等写服务应避免重试,或者可以考虑提前生成唯一流水号来保证写服务操作通过判断流水号来实现幂等操作。

在进行数据库/缓存服务器操作时,记得经常检查慢查询,慢查询通常是引起服务出问题的罪魁祸首。也要考虑在超时严重时,直接将该服务降级,待该服务修复后再取消降级。

对于有负载均衡的中间件,请考虑配置心跳/存活检查,而不是惰性检查。

超时重试必然导致请求响应时间增加,最坏情况下的响应时间=重试次数×单次超时时间,这很可能严重影响用户体验,导致用户不断刷新页面来重复请求,最后导致服务接收的请求太多而挂掉,因此除了控制单次超时时间,也要控制好用户能忍受的最长超时时间。

超时时间太短会导致服务调用成功率降低,超时时间太长又会导致本应成功的调用却失败了,这也要根据实际场景来选择最适合当前业务的超时时间,甚至是程序动态自动计算超时时间。比如商品详情页的库存状态服务,可以设置较短的超时时间,当超时时降级返回有货,而结算页服务就需要设置稍微长一些的超时时间保证确实有货。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值