1 Overview
常见的微服务架构:
微服务架构
做好超时时间的限定,用于判定超时后资源能够及时被释放,用于处理其它的请求,从而提升的性能。
前端:Ajax、Node
代理层:DNS、LB(SLB、F5、Keepalived+LVS、Haproxy、A10等)、Ngixn、Gateway
服务容器:Tomcat、Jetty
中间件:Feign、Dubbo、HTTPClient、ES、MongoDB、Redis
数据库:MySQL、Oracle
2 Solution
2.1 Front-end Timeout
2.1.1 ajax Timeout
ajax底层使用的是XMLHttpRequest,其超时参数可以设置:连接超时、读超时和写超时。但对于包装后的ajax,我们通常只需要设置请求超时时间(timeout)即可,具体案例如下:
var ajaxTimeoutTest = $.ajax({
url:'/demo', // 设置请求超时时间(毫秒),此设置将覆盖全局设置 timeout : 1000,
type : 'get',
data :{},
dataType:'json',
success:function(data){
alert("成功");
},
complete : function(XMLHttpRequest,status){ // 超时处理: status还有success,error等值的情况
if(status=='timeout'){
ajaxTimeoutTest.abort();
alert("超时");
}
}
});2.1.2 Node.js Timeout
Server Timeout
const http = require("http");const server = http.createServer( function(req, res){ // ......});// 设置服务端请求处理的超时时间server.setTimeout(30 * 1000);
server.listen(3000, "localhost", function(){ console.log("开始监听"+server.address().port+"......");
});Client Timeout
const http = require('http');const options = {host: 'localhost', method: 'GET', port: 8080, path: '/test'}var req = http.request(options);// 设置客户端每个外调的超时时间req.setTimeout(20 * 1000);
req.on('response', (res) => {
res.setEncoding('utf8');
res.on('data', function(chunk){ console.log('收到数据:%s', chunk);
});
res.on('end', function(){ console.log(res.trailers);
});
});
req.end();2.2 Ngixn Timeout
2.2.1 keepalive_timeout
HTTP是一种无状态协议,其客户端底层向服务器发送一个TCP请求,服务端响应完毕后就会断开连接。如果客户端向服务器发送多个请求,每个请求都要建立各自独立的连接以传输数据。
HTTP的KeepAlive就用于告诉服务器在处理完请求后保持一段这个TCP连接的打开状态。若接收到来自客户端的其它请求,服务端会利用这个未被关闭的连接,而不需要再建立一个连接。KeepAlive在一段时间内保持打开状态,它们会在这段时间内占用资源,但占用过多就会影响性能。
因此,Nginx使用 keepalive_timeout 来指定KeepAlive的超时时间,用于指定每个TCP 连接最多可以保持多长时间。Nginx的默认值是75 秒,然而有些浏览器最多只保持 60秒,所以可以设定为 60 秒 更安全。若将它设置为 0,就禁止了 keepalive 连接。
# 配置段: http、server、location, 默认值是75秒keepalive_timeout 60s;
2.2.2 client_body_timeout
用于指定客户端与服务端建立连接后发送 request body 的超时时间,如果客户端在指定时间内没有发送一个完整的 request body,Nginx就会返回 HTTP 408(Request Timed Out)。
# 配置段: http、server、locationclient_body_timeout 20s;
2.2.3 client_header_timeout
客户端向服务端发送一个完整的 request header 的超时时间,如果客户端在指定时间内没有发送一个完整的 request header,Nginx 返回 HTTP 408(Request Timed Out)。
# 配置段: http、server、locationclient_header_timeout 10s;
2.2.4 proxy_upstream_fail_timeout
fail_timeout通常是配合max_fails一起来使用的,实现熔断隔离的功能。其作用主要是指在 30 秒内请求某一应用失败 3 次,则认为该应用宕机,之后会等待 30 秒,这期间内不会再把新请求发送到宕机应用,而是直接发到正常的那一台。时间到后再有请求进来,则继续尝试连接宕机应用且仅尝试 1 次,如果还是失败,则继续等待 30 秒…...以此循环,直到恢复。
# 配置段: upstream, fail_timeout默认为10s, max_fails默认为1upstream web_tomcat { server 127.0.0.1:8081 max_fails=3 fail_timeout=30s; server 127.0.0.1:8082 max_fails=3 fail_timeout=30s;
}2.2.5 proxy_connect_timeout
用于设置Nginx向后端服务器的连接超时时间,即为发起TCP握手等候响应的超时时间。
# 配置段: http、server、location, 默认为60slocation / { proxy_connect_timeout 500s; proxy_pass http://web_tomcat;
}2.2.6 proxy_read_timeout
连接成功后,等候后端服务器响应时间,其实已经进入后端的排队之中等候处理,也可以说是后端服务器处理请求的 时间。
# 配置段: http、server、location, 默认为60slocation / { proxy_read_timeout 500s; proxy_pass http://web_tomcat;
}2.2.7 proxy_send_timeout
用于设置后端服务器数据回传时间,就是在规定时间之内后端服务器必须传完所有的数据。
# 配置段: http、server、location, 默认为60slocation / { proxy_send_timeout 500s; proxy_pass http://web_tomcat;
}2.2.8 Others
resolver_timeout:域名解析超时,默认30s。配置段:http、server、location
lingering_timeout:设置TCP连接关闭时的SO_LINGER延时,默认为5s。配置段:http、server、location
tcp_nodelay:默认情况下,当数据发送时,内核并不会马上发送,可能会等待更多的字节组成一个数据包,这样可以提高 I/O 性能,但是在每次只发送很少字节的业务场景中,等待时间会比较长
注意事项:
客户端连接Nginx超时,建议5s内
proxy_connect_timeout的值不能超过75s
通常client_body_timeout应该比keepalive_timeout小
扩展:tcp_nodelay与tcp_nopush
tcp_nodelay:开启或关闭Nginx使用TCP_NODELAY选项的功能
tcp_nopush:开启或者关闭Nginx在FreeBSD上使用TCP_NOPUSH套接字选项的功能
# tcp_nodelay配置段: http、server、location, 默认值为 tcp_nodelay on;# tcp_nopush配置段: http、server、location, 默认值为 tcp_nopush off;http { tcp_nodelay on;
}2.3 Gateway Timeout
2.3.1 Zuul Timeout
使用Ribbon路由
Zuul的超时与Ribbon、Hystrix相关(RibbonRoutingFilter整合了Hystrix和Ribbon),此时Zuul的超时可以配置如下:# Hystrix,设置调用者等待命令执行的超时限制,超过此时间,HystrixCommand被标记为TIMEOUT,并执行回退逻辑hystrix.command.xxx.execution.isolation.thread.timeoutInMilliseconds: 1000# Ribbonribbon: read-timeout: 1000 connect-timeout: 1000
未使用Ribbo路由(SimpleHostRoutingFilter整合了Apache HttpClient)
zuul.routes.xxx.path: /user/**zuul.routes.xxx.url: http://localhost:8000/# TCP连接超时时间zuul.host.connect-timeout-millis: 2000# Socket超时,即数据传输的超时时间zuul.host.socket-timeout-millis: 10000
2.4 Middleware Timeout
2.4.1 Ribbon Timeout
全局配置:
ribbon: read-timeout: 60000 connect-timeout: 60000
局部配置:
service-id: ribbon: read-timeout: 1000 connect-timeout: 1000
2.4.2 Feign Timeout
从Spring Cloud Edgware开始,Feign支持使用属性配置超时(对于老版本,可以写个feign.Request.Options 即可):
feign.client.config: feign-name: connect-timeout: 5000 read-timeout: 5000
2.4.3 RestTemplate Timeout
@Bean@LoadBalancedpublic RestTemplate restTemplate() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(1000);
factory.setReadTimeout(1000); return new RestTemplate(factory);
}2.4.4 Hystrix Timeout
# 默认开启超时机制hystrix.command.default|xxx.execution.timeout.enabled: true# 是否打开超时线程中断, Thread模式有效hystrix.command.default|xxx.execution.isolation.thread.interruptOnTimeout: true# 超时时间, 默认为1秒:# 1.在THREAD模式下,达到超时时间,可以中断# 2.在SEMAPHORE模式下,会等待执行完成后,再去判断是否超时hystrix.command.default|xxx.execution.isolation.thread.timeoutInMilliseconds: 1000
2.4.5 Tomcat Timeout
tomcat对每个请求的超时时间是通过connectionTimeout参数设置的。默认的server.xml里的设置是20秒,如果不设置这个参数代码里会使用60秒。这个参数也会对POST请求有影响,但并不是指上传完的时间限制,而是指两次数据发送中间的间隔超过connectionTimeout会被服务器断开。
<Connector port="7001" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
如果connectionTimeout配置为20000,这个配置导致建立一个socket连接后,如果一直没有收到客户端的FIN,也没有数据过来,那么此连接也必须等到20s后,才能被超时释放。
2.4.6 Dubbo Timeout
Dubbo协议超时实现使用了Future模式。ResponseFuture.get()在请求还未处理完或未到超时前一直是wait状态;响应达到后,设置请求状态,并进行notify唤醒。即使用了Object的 await-notify-notifyAll 机制。
Dubbo Architecture
Dubbo消费端
全局超时配置
<dubbo:consumer timeout="5000" />
指定接口以及特定方法超时配置
<dubbo:reference interface="com.foo.BarService" timeout="2000"> <dubbo:method name="sayHello" timeout="3000" /></dubbo:reference>
Dubbo服务端
全局超时配置
<dubbo:provider timeout="5000" />
指定接口以及特定方法超时配置
<dubbo:provider interface="com.foo.BarService" timeout="2000"> <dubbo:method name="sayHello" timeout="3000" /></dubbo:provider>
2.5 DB Timeout
以下是应用(WAS/BLOC)、连接池(DBCP)、Timeout层级和DBMS直接的关系图:
应用&数据库间 — Timeout架构
解释说明:
statement timeout无法处理网络连接失败时的超时,它能做的仅仅是限制statement的操作时间
网络连接失败时的timeout必须交由JDBC来处理
JDBC的socket timeout会受到操作系统socket timeout设置的影响
timeout层级与DBCP是相互独立,DBCP负责的是数据库连接的创建和管理,并不干涉timeout的处理
在应用中调用DBCP的getConnection()时,你可以设置获取数据库连接的超时时间,但是这和JDBC的timeout无关
案例:JDBC连接会在网络出错后阻塞30分钟,然后又奇迹般恢复,即使并没有对JDBC的socket timeout进行设置
2.5.1 Transaction Timeout
一般存在于框架或应用级,用于设置是一个事务的执行总时间,其中可能包含多个statement。在Spring中可以使用XML或在源码中使用@Transactional注解来进行设置。
1个statement ~ 0.1s,10w个statement ~ 1w秒(约7个小时)
1个statement × 1个statement执行200ms,则transaction timeout至少应该设置为:1100ms(200×5+100)
2.5.2 Statement Timeout
用于设置单个statement的执行超时时间,即Driver等待statement执行完成,接收到数据的超时时间。timeout的值通过调用JDBC的java.sql.Statement.setQueryTimeout(int timeout) API进行设置,但更多的是是通过框架来进行设置。
注意:
statement timeout的具体值需要依据应用本身的特性而定,并
没有可供推荐的配置statement的timeout不是整个查询的timeout,只是statement执行完成并拉取数据返回的超时时间
MySQL JDBC Statement的QueryTimeout处理过程
MySQL JDBC Statement的QueryTimeout处理过程
解释说明:
statement创建一个新的timeout-execution线程用于超时处理,5.1版本后改为每个connection分配一个timeout-execution线程
达到超时时间,TimerThread调用JtdsStatement实例中的TsdCore.cancel()方法,timeout-execution线程创建一个和statement配置相同的connection,向超时query发送:
cancel query(KILL QUERY “connectionId”)
2.5.3 JDBC socket timeout
用于设置jdbc I/O socket read and write operations的超时时间,防止因网络问题或数据库问题,导致Driver会一直阻塞等待。(建议比statement timeout的时间长)
mysql(单位为毫秒)
jdbc:mysql://localhost:3306/ag_admin?useUnicode=true&characterEncoding=UTF8&connectTimeout=60000&socketTimeout=60000
pg(单位为秒)
jdbc:postgresql://localhost/test?user=fred&password=secret&&connectTimeout=60&socketTimeout=60
oracle
oracle需要通过oracle.jdbc.ReadTimeout参数来设置,连接超时参数是oracle.net.CONNECT_TIMEOUT。可以通过以下两种方式进行设置:通过properties设置
Class.forName("oracle.jdbc.driver.OracleDriver"); Properties props = new Properties() ; props.put( "user" , "test_schema") ; props.put( "password" , "pwd") ; props.put( "oracle.net.CONNECT_TIMEOUT" , "10000000") ; props.put( "oracle.jdbc.ReadTimeout" , "2000" ) ; Connection conn = DriverManager.getConnection( "jdbc:oracle:thin:@127.0.0.1:1521:orcl" , props ) ;通过环境变量设置 —— 注意需要在connection连接之前设置环境变量
String readTimeout = "10000"; // msSystem.setProperty("oracle.jdbc.ReadTimeout", readTimeout); Class.forName("oracle.jdbc.OracleDriver"); Connection conn = DriverManager.getConnection(jdbcUrl, user, pwd);
2.5.4 OS socket timeout
这是操作系统级别的socket设置,用来检测坏死socket连接,Linux一般默认2小时。如果jdbc socket timeout没有设置,而OS级别的socket timeout有设置,则使用系统的socket timeout值。
# 查看OS的keepalive配置信息sudo sysctl -a|grep keepalive# 修改OS的keepalive配置信息,并修改以下配置信息vim /etc/sysctl.conf# 表示TCP连接在多少秒之后没有数据报文传输时启动探测报文(发送空的报文),单位为秒(s)net.ipv4.tcp_keepalive_time = 7200# 表示前一个探测报文和后一个探测报文之间的时间间隔,单位为秒(s)net.ipv4.tcp_keepalive_intvl = 75# 表示探测的次数net.ipv4.tcp_keepalive_probes = 9# 让修改的参数即时生效sysctl -p
总结
jdbc的socketTimeout值的设置要非常小心,不同数据库的jdbc driver设置不一样,特别是使用不同连接池的话,设置也可能不尽相同。对于严重依赖数据库操作的服务来说,非常有必要设置这个值,否则万一网络或数据库异常,会导致服务线程一直阻塞在java.net.SocketInputStream.socketRead0。
如果查询数据多,则会导致该线程持有的data list不能释放,相当于内存泄露,最后导致OOM
如果请求数据库操作很多且阻塞住了,会导致服务器可用的woker线程变少,严重则会导致服务不可用
3 Practice
3.1 Focus
各层组件的超时时间,主要是设置以下两个参数:
connectTimeout
socketTimeout
当然针对特殊的场景,则可以设置更详细的超时参数,如:
readTimeout
writeTimeout
3.2 Suggest
ajax —— 5s ~ 60s
建议全局设置一个统一的超时时间,如60s
从使用的互联网产品来看,一般网络较差时,加载网页可能需要等待30秒或1分钟左右后才出现网络异常等的情况
特殊场景自定义设置超时时间,从而覆盖全局超时时间
如上传较大文件时,则可以设置时间更长(当然太大的文件,则建议单独考虑,如分块处理等)
如实时性要求较高的场景,则可以设置更短,如5s等
Ngixn
建议设置
keepalived_time来提高Ngixn支持的并发能力与复用HTTP建立的TCP连接,如设置为5s建议设置
client_body_timeout和client_header_timeout,用于防止客户攻击Dos攻击,如分别20s、10s建议设置
max_fails和fail_timeout,解决每次请求宕机服务端时,都需要等待超时问题,如分别为3次、30s建议设置
proxy_connect_timeout、proxy_send_timeout和proxy_read_timeout参数,用于控制Ngixn转发到后台的超时控制
Node
使用Node作为网关代理转发请求时:
Server —— 60s
如果代理层有一定的功能逻辑,则建议加上Server的处理超时时间
如果代理层几乎没有逻辑,则Server层的超时可以不配置
Client
Client用于代理转发,而后端业务场景不同,要求也有所不同,所以建议设置较长的默认值,并支持请求自定义
Middleware
Ribbon、Zuul(Apache HTTPClient)、Feign、RestTemplate和Netty等,都建议必须设置以下两个参数:
connectTimeout
socketTimeout
Hystrix
使用Hystrix时,建议设置提交线程后的等待超时时间:thread.timeoutInMilliseconds ,默认为1000ms
DB
Transaction Timeout
connectTimeout
socketTimeout
OS socket timeout
3.3 Timeout Architecture
微服务超时架构
作者:李景枫
链接:https://www.jianshu.com/p/3863a5f1e79d
共同学习,写下你的评论
评论加载中...
作者其他优质文章




