在实际开发当中,很多故障的产生都是因为没有意识到超时设置的重要性而造成的。如果不设置超时机制,就有可能导致连锁反应,最终系统雪崩。有些中间件天然集成了超时重试的机制,但是在设计当中需要注意的是:读服务非常适合重试,但是写服务大多不能重试。为了解决写操作的重试问题,一般情况下会使用“幂等重试”的方式。但是跨系统调用又不能无限次数的重试,这里面就需要设置合理的重试次数,并且配合熔断、快速失败等机制。
重试的分类
从系统调用的整个链路来看,可以将其分为以下几种类型。
代理层超时重试:如Haproxy、Nginx等。这里面需要合理的设置代理与后端真实服务之间的网络连接/读/写的超时时间。
Web容器超时:如Tomcat、Jetty等。应用服务器需要设置客户端与容器之间的网络连接/读/写超时时间。以及默认Socket网络连接/读/写的超时时间。
中间件客户端超时重试:如Dubbo、HTTPClient等。需要设置客户端的网络连接/读/写超时时间与重试机制。
数据库客户端超时:如MySQL、MongoDB、Redis等。需要分别设置JDBC Connection、Statement的网络连接/读/写超时时间,事务超时时间,获取连接池连接等待时间。
前端Ajax超时:浏览器通过Ajax访问时的网络连接/读/写超时时间。
Nginx代理层超时
Nginx(OpenResty)相关的主要有4类超时设置:客户端超时设置、DNS解析超时设置、代理超时设置和ngx_lua相关的超时设置。
客户端超时设置
对于客户端的超时设置有:读取请求头超时时间、读取请求体超时时间、发送响应超时时间、长连接超时时间。通过客户端的超时时间设置,可以避免一些长连接的占用,以至于影响服务端的性能。
client_header_timeout time:设置读取客户端请求头超时时间,默认为60S。如果在此时间内,客户端没有发送完请求头,则响应408(Request Time-out)状态码。
client_body_timeout time:设置读取客户端请求体超时时间,默认60S。此时间是指2次成功读操作的时间间隔(针对于Nginx本身而言的),而不是客户端发送的时间。超时则响应408状态。
send_timeout time:设置发送响应到客户端的超时时间,默认60S。此处的时间也是2次写操作的时间间隔。如果在此时间内,客户端没有接收任何响应,Nginx就会关闭连接。
keepalive_timeout timeout [header_timeout]:设置HTTP长连接超时时间。其中第一个参数(timeout)是告诉Nginx长连接的超时时间是多少,默认是75S;第二个参数header_timeout用于设置响应头“Keep-Alive: timeout=time”,即告诉客户端长连接超时时间。
DNS解析超时设置
resolver_timeout 30s:设置DNS解析超时时间,默认30S。它需要配合resolver address [valid=time]进行DNS域名解析。
其中当upstream中配置如下时,每次Nginx启动时,都会把domain解析成IP。如果如果此时IP变更,Nginx不会立马更新(社区版本不会立即更新,商业版本可以),只有在valid过期后,才会刷新。
如果要解决这个问题,可以在proxy_pass中如下设置,但是当多域名时,就比较麻烦了。
如果用的是OpenResty,则可以使用lua-resty-dns进行DNS解析。
代理超时设置
Nginx配置如下
backend_server定义了2个上游服务器。上面的配置主要有3组配置:网络连接/读/写超时设置、失败重试机制设置、upstream存活超时设置。
网络连接/读/写超时设置
proxy_connect_timeout time:与后端/上游服务器建立连接的超时时间,默认为60s。
proxy_read_timeout time:设置从后端/上游服务器读取响应的超时时间,默认60s。此超时时间指的是2次成功读操作时间间隔,而不是读取整个响应体的超时时间。如果超时时间内没有任何响应,则Nginx将关闭连接。
proxy_send_timeout time:设置往后端/上游服务器发送请求的超时时间,默认60s。此超时时间指的是2次成功读操作时间间隔,而不是读取整个响应体的超时时间。如果超时时间内没有任何响应,则Nginx将关闭连接。
失败重试机制设置
proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | non_idempotent | off ... :配置什么情况下需要向上游服务器进行重试。默认为:error timeout。error表示读写出错;timeout表示超时;invalid_header表示头信息有误;non_idempotent表示RFC-2616定义的非幂等HTTP方法(POST、LOCK、PATCH),也可以在失败后重试(默认幂等方法GET、HEAD、PUT、DELETE、OPTIONS、TRACE);off表示禁用重试。
proxy_next_upstream_tries number:设置重试次数,默认0表示不限制。这里的次数包含第一次请求。
proxy_next_upstream_timeout time:设置重试最大超时时间,默认0表示不限制。
上面2个值的关系是且的关系,在限制时间内或者重试次数达到一个值就会结束重试,并返回客户端响应。
upstream存活超时设置
max_fails和fail_timeout:配置什么情况下Nginx将上有服务器认定为不可用。条件为:在fail_timeout时间内失败了max_fails次数。例如上面的配置中,10S失败次数达到2次,就认为上游服务器不可用。
若max_fails设置为0,则表示不检查是否不可用。
ngx_lua超时设置
当我们在使用ngx_lua时,需要考虑的超时设置如下:
Web容器超时
Web容器的设置,下面以Tomcat为例。
connectionTimeout:设置与客户端简历连接的超时时间。从接收到连接后,在配置的时间内没有接收到客户端请求行,就会被认为连接超时,默认60S。
socket.soTimeout:从客户端读取请求数据的超时时间。默认60S,NIO和NIO2支持该配置。
asyncTimeout:Servlet3异步请求的超时时间,默认30S。
disableUploadTimeout和connectionUploadTimeout:当配置disableUploadTimeout为false时(默认为true,和connectTimeout一样),文件上传将使用connectionUploadTimeout作为超时时间。
keepAliveTimeout和maxKeepAliveRequests:keepAliveTimeout默认为connectionTimeout,配置-1表示永不超时。maxKeepAliveRequests默认为100。
参考:《亿级流量网站架构核心技术》