当前位置: 首页 > news >正文

Micro Service Architecture — Timeout

为什么80%的码农都做不了架构师?>>>   hot3.png

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、location
client_body_timeout 20s;

2.2.3 client_header_timeout

​ 客户端向服务端发送一个完整的 request header 的超时时间,如果客户端在指定时间内没有发送一个完整的 request header,Nginx 返回 HTTP 408(Request Timed Out)

# 配置段: http、server、location
client_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默认为1
upstream  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, 默认为60s
location / {
  	proxy_connect_timeout 500s;
    proxy_pass http://web_tomcat;
 }

2.2.6 proxy_read_timeout

​ 连接成功后,等候后端服务器响应时间,其实已经进入后端的排队之中等候处理,也可以说是后端服务器处理请求的 时间。

# 配置段: http、server、location, 默认为60s
location / {
  	proxy_read_timeout 500s;
    proxy_pass http://web_tomcat;
 }

2.2.7 proxy_send_timeout

​ 用于设置后端服务器数据回传时间,就是在规定时间之内后端服务器必须传完所有的数据。

# 配置段: http、server、location, 默认为60s
location / {
	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
    
    # Ribbon
    ribbon:
     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
@LoadBalanced
public 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"; // ms
    System.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_timeoutclient_header_timeout,用于防止客户攻击Dos攻击,如分别20s、10s
  • 建议设置 max_failsfail_timeout ,解决每次请求宕机服务端时,都需要等待超时问题,如分别为3次、30s
  • 建议设置 proxy_connect_timeoutproxy_send_timeoutproxy_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://my.oschina.net/yu120/blog/1925648

相关文章:

  • 通用清除浮动方式
  • Struts2之校验
  • python基础库
  • 逻辑回归最小二乘推导
  • Emgu-WPF 激光雷达研究-移动物体跟踪
  • 浅谈OSI七层模型之物理层
  • 详述 IntelliJ IDEA 插件的安装及使用方法
  • python3 之__str__
  • CentOS Peer's Certificate has expired 问题
  • Golang dep包管理器
  • iptables1
  • 完美Python入门基础知识点总结,看完你的Python就完全入门了!
  • canvas下雨特效
  • Confluence 6 为登录失败配置使用验证码
  • [Python设计模式] 第27章 正则表达式——解释器模式
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • 2017届校招提前批面试回顾
  • Android单元测试 - 几个重要问题
  • css属性的继承、初识值、计算值、当前值、应用值
  • input实现文字超出省略号功能
  • Java Agent 学习笔记
  • javascript数组去重/查找/插入/删除
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • React-Native - 收藏集 - 掘金
  • 关于Java中分层中遇到的一些问题
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 如何在GitHub上创建个人博客
  • 手机端车牌号码键盘的vue组件
  • 微信小程序:实现悬浮返回和分享按钮
  • 详解NodeJs流之一
  • 学习HTTP相关知识笔记
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • 原生Ajax
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • #1015 : KMP算法
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (pojstep1.3.1)1017(构造法模拟)
  • (附源码)spring boot儿童教育管理系统 毕业设计 281442
  • (附源码)springboot金融新闻信息服务系统 毕业设计651450
  • (力扣记录)235. 二叉搜索树的最近公共祖先
  • (四)linux文件内容查看
  • (小白学Java)Java简介和基本配置
  • (一)appium-desktop定位元素原理
  • .gitignore
  • .NET Core日志内容详解,详解不同日志级别的区别和有关日志记录的实用工具和第三方库详解与示例
  • .Net IE10 _doPostBack 未定义
  • .NET MVC之AOP
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .net 程序 换成 java,NET程序员如何转行为J2EE之java基础上(9)
  • .net开发引用程序集提示没有强名称的解决办法
  • .py文件应该怎样打开?
  • /etc/fstab和/etc/mtab的区别
  • [ CTF ] WriteUp- 2022年第三届“网鼎杯”网络安全大赛(朱雀组)