搞懂connectTimeout和socketTimeout的区别

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: 搞懂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来搞的。

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


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
Java
Java 清空 List 的多种方法?
Java 清空 List 的多种方法?
2787 0
|
druid 关系型数据库 MySQL
【数据库】数据库连接池配置 testOnBorrow
【数据库】数据库连接池配置 testOnBorrow
1169 0
|
druid Java 数据库连接
数据库连接池及Druid使用步骤
数据库连接池及Druid使用步骤
1516 2
|
SQL Java 数据库连接
一篇文章彻底理解数据库的各种 JDBC 超时参数 1
一篇文章彻底理解数据库的各种 JDBC 超时参数
|
SQL druid Java
线程池相关故障问题之Druid数据库连接池中,为何需要设置TransactionTimeout
线程池相关故障问题之Druid数据库连接池中,为何需要设置TransactionTimeout
489 0
|
4月前
|
算法 网络协议 Java
Spring Boot 的接口限流算法
本文介绍了高并发系统中流量控制的重要性及常见的限流算法。首先讲解了简单的计数器法,其通过设置时间窗口内的请求数限制来控制流量,但存在临界问题。接着介绍了滑动窗口算法,通过将时间窗口划分为多个格子,提高了统计精度并缓解了临界问题。随后详细描述了漏桶算法和令牌桶算法,前者以固定速率处理请求,后者允许一定程度的流量突发,更符合实际需求。最后对比了各算法的特点与适用场景,指出选择合适的算法需根据具体情况进行分析。
388 56
Spring Boot 的接口限流算法
|
网络协议 Java 关系型数据库
一篇文章彻底理解数据库的各种 JDBC 超时参数 2
一篇文章彻底理解数据库的各种 JDBC 超时参数
|
12月前
|
SQL 存储 分布式计算
ODPS技术架构深度剖析与实战指南——从零开始掌握阿里巴巴大数据处理平台的核心要义与应用技巧
【10月更文挑战第9天】ODPS是阿里巴巴推出的大数据处理平台,支持海量数据的存储与计算,适用于数据仓库、数据挖掘等场景。其核心组件涵盖数据存储、计算引擎、任务调度、资源管理和用户界面,确保数据处理的稳定、安全与高效。通过创建项目、上传数据、编写SQL或MapReduce程序,用户可轻松完成复杂的数据处理任务。示例展示了如何使用ODPS SQL查询每个用户的最早登录时间。
1395 1
|
SQL druid Java
解决 ‘The last packet successfully received from the server was xxx milliseconds ago‘ 问题
解决 ‘The last packet successfully received from the server was xxx milliseconds ago‘ 问题
6526 0
|
SQL Java 数据库连接
成功解决:was not registered for synchronization because synchronization is not active
这篇文章是关于解决Mybatis在同步过程中出现"was not registered for synchronization because synchronization is not active"错误的技术博客。
成功解决:was not registered for synchronization because synchronization is not active