搞懂connectTimeout和socketTimeout的区别

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介: 搞懂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 
目录
相关文章
|
SQL Java 数据库连接
一篇文章彻底理解数据库的各种 JDBC 超时参数 1
一篇文章彻底理解数据库的各种 JDBC 超时参数
|
3月前
|
SQL 存储 数据管理
七、深入 Hive DDL:管理表、分区与洞察元数据
在日常使用 Hive 的过程中,我们不仅要会建表,更要学会灵活地维护和管理已有的数据结构。从添加字段到修改分区,从查看元数据到删除表或清空数据,掌握这些 DDL 操作和常用的 SHOW 命令,就像掌握了一套管理数据仓库的“万能钥匙”。这次将带你一步步熟悉这些命令的用法和实际应用场景,配合清晰的语法示例与练习题,帮助你更轻松地驾驭 Hive 数据管理的日常工作。
387 6
|
SQL druid Java
线程池相关故障问题之Druid数据库连接池中,为何需要设置TransactionTimeout
线程池相关故障问题之Druid数据库连接池中,为何需要设置TransactionTimeout
957 0
|
9月前
|
算法 网络协议 Java
Spring Boot 的接口限流算法
本文介绍了高并发系统中流量控制的重要性及常见的限流算法。首先讲解了简单的计数器法,其通过设置时间窗口内的请求数限制来控制流量,但存在临界问题。接着介绍了滑动窗口算法,通过将时间窗口划分为多个格子,提高了统计精度并缓解了临界问题。随后详细描述了漏桶算法和令牌桶算法,前者以固定速率处理请求,后者允许一定程度的流量突发,更符合实际需求。最后对比了各算法的特点与适用场景,指出选择合适的算法需根据具体情况进行分析。
845 56
Spring Boot 的接口限流算法
|
网络协议 Java 关系型数据库
一篇文章彻底理解数据库的各种 JDBC 超时参数 2
一篇文章彻底理解数据库的各种 JDBC 超时参数
|
10月前
|
安全
区分ConnectTimeout错误和ConnectionError异常在使用中的不同。
总结一下,这两个问题就像是你试图拨打一个电话。ConnectTimeout错误就好比你拨通了电话,但另一端没有接听;ConnectionError异常则好比你的电话根本没法拨出去,或者你拨错了号码——甚至是你根本就没拿到电话。所以,在下一次遇到类似问题的时候,希望你能想起这个比喻,然后灵活应对。
441 34
|
前端开发 Java Spring
SpringBoot2.6.x 整合swagger3.0 报错Failed to start bean ‘documentationPluginsBootstrapper‘
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
16554 3
SpringBoot2.6.x 整合swagger3.0 报错Failed to start bean ‘documentationPluginsBootstrapper‘
|
存储 编解码 监控
|
NoSQL MongoDB 数据库
MongoDB数据恢复—MongoDB数据库文件被破坏的数据恢复案例
服务器数据恢复环境: 一台Windows Server操作系统服务器,服务器上部署MongoDB数据库。 MongoDB数据库故障&检测: 工作人员在未关闭MongoDB数据库服务的情况下,将数据库文件拷贝到其他分区。拷贝完成后将原MongoDB数据库所在分区进行了格式化操作,然后将数据库文件拷回原分区,重新启动MongoDB服务,服务无法启动。

热门文章

最新文章