搞懂connectTimeout和socketTimeout的区别

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 搞懂connectTimeout和socketTimeout的区别

背景

有时候,由于业务的复杂性,在JVM中拼装一些数据,会造成资源的极大浪费。举个例子,从MySQL中查询出一个List,然后在代码里循环查询数据库,进行一些字段的填充。

这种数据组装方式,除了执行效率的问题,往往会有更多的内存占用,对整个JVM计算节点造成了比较大的压力,有时候甚至造成内存溢出。于是,一些比较牛X的开发人员,使用非常复杂的SQL,来把这些耗时的操作,转嫁给数据库。

可怜的数据库,成了最后一道屏障。谁让数据库的配置普遍都比较高呢?活该。

但是可惜的是,数据库完成这些动作,同样要经历耗时的操作。Java线程等的不耐烦了,就会对用户直接返回超时,懵逼的用户会在这种情况下,再次发起重试。

要知道,Java端超时,并不代表发起的请求就结束运行了,这在一些高并发的场景中,可怜的数据库会空跑一些耗时的慢查询,计算着一些无人能知的数据。

可怜的数据库。

1、如何设置数据库超时时间

对于mysql数据库,有两个可用的参数:

connectTimeout

默认值:0,单位:毫秒配置连接超时时间,通过 Socket 对象的 connect(SocketAddress endpoint, int timeout) 方法来配置

socketTimeout

默认值:0,单位:ms配置socket的超时时间,通过 Socket 对象的 setSoTimeout(int timeout) 方法来配置

示例:

jdbc:mysql://xxx.xx.xxx.xxx:3306/database?connectTimeout=60000&socketTimeout=60000

2.二者区别

1 connectTimeout与socketTimeout

connect timeout和socket timeout都属于TCP层面的超时。

以mysql为例,我们可以在jdbc url中指定connectTimeout和socketTimeout。如:

jdbc:mysql://localhost:3306/db?connectTimeout=1000&socketTimeout=60000

其中:

  • connectTimeout:表示的是数据库驱动(mysql-connector-java)与mysql服务器建立TCP连接的超时时间。
  • socketTimeout:是通过TCP连接发送数据(在这里就是要执行的sql)后,等待响应的超时时间。

mysql驱动(mysql-connector-java)在与服务端建立Socket连接时,会将这两个参数设置到socket对象上参见:

提示:这里的mysqlConnection类型为java.net.Socket

如果这两个参数设置的不够合理,都会导致mysql驱动抛出以下异常:

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
相信大部分读者对这个异常都不陌生。接下来笔者将分别演示这两个异常是如何产生的,并提出对应的解决方案。

1.1 connectTimeout

下面首先通过一个案例演示如何模拟connectTimeout

@Testpublic void testConnectTimeout() throws SQLException {    DruidDataSource dataSource = new DruidDataSource();    dataSource.setInitialSize(5);    dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test?connectTimeout=5");    dataSource.setUsername("root");    dataSource.setPassword(“your password");    dataSource.setDriverClassName("com.mysql.jdbc.Driver”);    dataSource.init();//初始化,底层通过mysql-connector-java建立数据库连接}

笔者这里将connectTimeout设置为了5ms,表示mysql驱动与服务端建立一个连接最多不能超过5ms。由于这里是与本地(127.0.0.1)数据库建立一个连接,5ms已经足够。然而,如果你是与一个远程数据库建立连接,那么5ms可能无法完成建立一个连接,此时你极有可能会遇到类似以下异常:

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failureThe last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)    ...Caused by: java.net.SocketTimeoutException: connect timed out    at java.net.PlainSocketImpl.socketConnect(Native Method)    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)    ...

到这里,我们看到了:

CommunicationsException异常,异常的Caused by部分是

java.net.SocketTimeoutException: connect timed out

也就是说,建立底层socket 连接超时了。这通常意味着我们需要将connectTimeout值调大。

这个问题并非无关紧要,特别是在公司有多个数据中心的情况下,尤其需要注意。笔者曾经遇到过有业务开发同学,应用部署在北京,数据库集群在北京和上海都有部署,如下图:

1.2 socketTimeout

socket timeout是我们实际开发中最容易遇到的另外一个导致CommunicationsException异常的原因,通常是在sql的执行时间超过了socket timeout设置的情况下出现。例如socket timeout设置的是3s,但是sql执行确需要5s,那么将会出现异常。

socket timeout异常演示:

@Test   public void testSocketTimeout() throws SQLException {   org.apache.tomcat.jdbc.pool.DataSource datasource = new org.apache.tomcat.jdbc.pool.DataSource();   //设置socketTimeout=3000,单位是ms   datasource.setUrl("jdbc:mysql://localhost:3306/test?socketTimeout=3000");
   datasource.setUsername("root");   datasource.setDriverClassName("com.mysql.jdbc.Driver");   datasource.setPassword(“your password");   Connection connection = datasource.getConnection();   PreparedStatement ps = connection.prepareStatement("select sleep(5)");   ps.executeQuery();}

在这个案例中,我们模拟了一个慢查询,通过执行"select sleep(5)",sleep是mysql提供的函数,其接受一个休眠时间,单位是s,当我们把这个sql发送给mysql时,mysql服务端会休眠5秒后,再返回结果。

然而,由于我们在jdbc url中设置了socketTimeout=3000,意味着单条sql最大执行时间不能超过3s。因此运行以上案例,将会抛出类似以下异常:

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failureThe last packet successfully received from the server was 3,080 milliseconds ago.  The last packet sent successfully to the server was 3,005 milliseconds ago.    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)    ...
Caused by: java.net.SocketTimeoutException: Read timed out    at java.net.SocketInputStream.socketRead0(Native Method)    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)    ...

这个异常看起来与connectTimeout导致的异常很相似,但是实际却有很大不同。这里我们是执行了一条sql,Caused By部分的异常提示为Read timed out,而之前是建立连接时抛出的异常,异常提示为connect timeout

在异常信息的开始部分,我们看到了详细的错误提示信息:最后一次接收到服务端返回的报文是3080ms之前,最后一次发送报文给服务端是3005ms之前。

细心的读者已经发现,3005ms与我们设置的socketTimeout=3000如此接近,事实上,你可以认为多出的5ms是系统检测到超过socketTimeout的耗时,之后抛出异常。当然,在实际开发中,系统检测socket timeout的耗时并不是固定为5ms,每次检测的耗时可能都不同,一般不过超过几十毫秒。

另外,socketTimeout是配置在jdbc url上的,对于所有执行的sql都会有这个超时限制。因此在配置这个值的时候,应该比应用中耗时最长的sql还要稍大一点。

socketTimeout默认值也是0,也就是不超时。

但是tcp的option只有so_timeout,那java socket的connect timeout咋来的呢?后来翻看了openjdk的代码才知道,java的connect timeout是用poll来搞的。

本篇文章如有帮助到您,请给「翎野君」点个赞,感谢您的支持。


相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
6月前
|
机器学习/深度学习 人工智能 运维
10 分钟搞懂 LLMOps
10 分钟搞懂 LLMOps
418 0
|
6月前
|
人工智能 网络协议 算法
5 分钟搞懂 ECN
5 分钟搞懂 ECN
666 0
|
2天前
|
C#
彻底搞懂 for、while、do...while
彻底搞懂 for、while、do...while
15 0
|
8月前
|
自然语言处理 前端开发 JavaScript
前端经典面试题 | 闭包的作用和原理
前端经典面试题 | 闭包的作用和原理
|
2天前
|
前端开发 JavaScript 索引
【面试题】for...in 和 for...of 的区别
【面试题】for...in 和 for...of 的区别
|
2天前
|
前端开发
前端知识笔记(四)———深浅拷贝的区别,如何实现?
前端知识笔记(四)———深浅拷贝的区别,如何实现?
32 0
|
6月前
|
人工智能 安全 Cloud Native
5 分钟搞懂 NESAS
5 分钟搞懂 NESAS
63 0
|
8月前
|
前端开发
前端经典面试题 | New操作符的原理
前端经典面试题 | New操作符的原理
|
9月前
面试题: &,| 与&&,||的区别?
面试题: &,| 与&&,||的区别?
30 0
|
10月前
|
Kubernetes 并行计算 负载均衡
一文搞懂 K3D
Hello folks,作为一款由 Google 开发的开源平台,Kubernetes 主要用于自动部署、资源扩展、管理以及编排容器化应用程序。其不仅是提供了一个简单的系统,用于管理跨多个服务器的容器,同时,具备出色的负载平衡和资源分配能力,以确保每个应用程序能够以最佳性能运行。
496 0