tidb节点重启后,服务无法重连

大家好,我是烤鸭:

    前几天遇到tidb节点重启后服务无法重连,确切地说是两个服务,一个可以正常重连,一个不行。

问题复现

由于线上执行慢SQL,导致TiDB 单个节点宕机重启。

其中A服务的3个节点和B服务的1个节点开始报错,重启后B服务恢复,A服务还在报错。

使用的组件版本:

A服务:tomcat-jdbc-9.0.71.jar
B服务:HikariCP-4.0.3.jar

数据库配置:

A服务:

spring.xxx-tidb-data-source.url=jdbc:mysql://xxx.xxx.xxx:6000/xxxA?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8

B服务:

spring.datasource.url=jdbc:mysql://xxx.xxx.xxx:6000/xxxB?useSSL=false&autoReconnect=true

可以看到两边的配置和连接池不同。

报错日志

无法重连服务的报错日志:

Caused by: com.mysql.cj.exceptions.ConnectionIsClosedException: No operations allowed after connection closed.at sun.reflect.GeneratedConstructorAccessor139.newInstance(Unknown Source)at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)at java.lang.reflect.Constructor.newInstance(Constructor.java:423)at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:105)at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:151)at com.mysql.cj.NativeSession.checkClosed(NativeSession.java:762)at com.mysql.cj.jdbc.ConnectionImpl.checkClosed(ConnectionImpl.java:569)at com.mysql.cj.jdbc.ConnectionImpl.prepareStatement$original$McwhO8C0(ConnectionImpl.java:1580)... 125 common frames omitted
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: The last packet successfully received from the server was 259,130,913 milliseconds ago. The last packet sent successfully to the server was 259,130,914 milliseconds ago.may or may not be greater than the server-side timeout (the driver was unable to determine the value of either the 'wait_timeout' or 'interactive_timeout' configuration values from the server.. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.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)at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:105)at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:151)at com.mysql.cj.exceptions.ExceptionFactory.createCommunicationsException(ExceptionFactory.java:167)at com.mysql.cj.protocol.a.NativeProtocol.readMessage(NativeProtocol.java:520)at com.mysql.cj.protocol.a.NativeProtocol.checkErrorMessage(NativeProtocol.java:700)at com.mysql.cj.protocol.a.NativeProtocol.sendCommand(NativeProtocol.java:639)at com.mysql.cj.protocol.a.NativeProtocol.sendQueryPacket(NativeProtocol.java:987)at com.mysql.cj.NativeSession.execSQL(NativeSession.java:666)at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:930)at com.mysql.cj.jdbc.ClientPreparedStatement.execute$original$mDKgOeFa(ClientPreparedStatement.java:371)at com.mysql.cj.jdbc.ClientPreparedStatement.execute$original$mDKgOeFa$accessor$UdYpLG4x(ClientPreparedStatement.java)at com.mysql.cj.jdbc.ClientPreparedStatement$auxiliary$BAL6Kq1Z.call(Unknown Source)at org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstMethodsInter.intercept(InstMethodsInter.java:86)at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java)at sun.reflect.GeneratedMethodAccessor261.invoke(Unknown Source)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:118)

问题猜想

找到变量了,就顺着猜测查一下。

  • 数据库配置参数 autoReconnect=true
  • 连接池 tomcat-jdbc 和 HikariCP

报错日志都来源于 CJCommunicationsException 这个类,其实调用的是 ExceptionFactory.createLinkFailureMessageBasedOnHeuristics,报错日志来源于配置文件。

我保留了一部分一会可能用到的变量

LocalizedErrorMessages.properties

CommunicationsException.2=\ is longer than the server configured value of 
CommunicationsException.3=''wait_timeout''
CommunicationsException.4=''interactive_timeout''
CommunicationsException.5=may or may not be greater than the server-side timeout 
CommunicationsException.6=(the driver was unable to determine the value of either the 
CommunicationsException.7=''wait_timeout'' or ''interactive_timeout'' configuration values from 
CommunicationsException.8=the server.
CommunicationsException.11=. You should consider either expiring and/or testing connection validity 
CommunicationsException.12=before use in your application, increasing the server configured values for client timeouts, 
CommunicationsException.13=or using the Connector/J connection property ''autoReconnect=true'' to avoid this problem.
CommunicationsException.20=Communications link failure...Connection.0=Unable to connect to database.
Connection.1=Cannot connect to MySQL server on {0}:{1}.\n\nMake sure that there is a MySQL server running on the machine/port you are trying to connect to and that the machine this software is running on is able to connect to this host/port (i.e. not firewalled). Also make sure that the server has not been started with the --skip-networking flag.\n\n
Connection.2=No operations allowed after connection closed.
Connection.3=Can''t call commit when autocommit=true
Connection.4=Communications link failure during commit(). Transaction resolution unknown.
Connection.5=Unknown Java encoding for the character set with index ''{0}''. Use the ''customCharsetMapping'' property to force it.
Connection.6=Unknown character set index ''{0}'' received from server. The appropriate client character set can be forced via the ''characterEncoding'' property.
Connection.7=Can''t map {0} given for characterSetResults to a supported MySQL encoding.
Connection.8=Unable to use encoding: {0}

看看源码

从报错日志入手,根据链接状态和上次发送、接收时间判断,拼一些异常原因。

可以看出报错日志有几个组成部分:

1、上次发送packet的时间距离现在超过8小时,getLastPacketSentTime > 28800秒(8小时)

2、当前链接已关闭:No operations allowed after connection closed

ExceptionFactory

public static String createLinkFailureMessageBasedOnHeuristics(PropertySet propertySet, ServerSession serverSession,PacketSentTimeHolder packetSentTimeHolder, PacketReceivedTimeHolder packetReceivedTimeHolder, Throwable underlyingException) {long serverTimeoutSeconds = 0;boolean isInteractiveClient = false;long lastPacketReceivedTimeMs = packetReceivedTimeHolder == null ? 0L : packetReceivedTimeHolder.getLastPacketReceivedTime();long lastPacketSentTimeMs = packetSentTimeHolder.getLastPacketSentTime();if (lastPacketSentTimeMs > lastPacketReceivedTimeMs) {lastPacketSentTimeMs = packetSentTimeHolder.getPreviousPacketSentTime();}if (propertySet != null) {isInteractiveClient = propertySet.getBooleanProperty(PropertyKey.interactiveClient).getValue();String serverTimeoutSecondsStr = null;if (serverSession != null) {serverTimeoutSecondsStr = isInteractiveClient ? serverSession.getServerVariable("interactive_timeout"): serverSession.getServerVariable("wait_timeout");}if (serverTimeoutSecondsStr != null) {try {serverTimeoutSeconds = Long.parseLong(serverTimeoutSecondsStr);} catch (NumberFormatException nfe) {serverTimeoutSeconds = 0;}}}StringBuilder exceptionMessageBuf = new StringBuilder();long nowMs = System.currentTimeMillis();if (lastPacketSentTimeMs == 0) {lastPacketSentTimeMs = nowMs;}long timeSinceLastPacketSentMs = (nowMs - lastPacketSentTimeMs);long timeSinceLastPacketSeconds = timeSinceLastPacketSentMs / 1000;long timeSinceLastPacketReceivedMs = (nowMs - lastPacketReceivedTimeMs);int dueToTimeout = DUE_TO_TIMEOUT_FALSE;StringBuilder timeoutMessageBuf = null;if (serverTimeoutSeconds != 0) {if (timeSinceLastPacketSeconds > serverTimeoutSeconds) {dueToTimeout = DUE_TO_TIMEOUT_TRUE;timeoutMessageBuf = new StringBuilder();timeoutMessageBuf.append(Messages.getString("CommunicationsException.2"));timeoutMessageBuf.append(Messages.getString(isInteractiveClient ? "CommunicationsException.4" : "CommunicationsException.3"));}// 上次发送成功的时间距离现在超过8小时} else if (timeSinceLastPacketSeconds > DEFAULT_WAIT_TIMEOUT_SECONDS) {dueToTimeout = DUE_TO_TIMEOUT_MAYBE;timeoutMessageBuf = new StringBuilder();timeoutMessageBuf.append(Messages.getString("CommunicationsException.5"));timeoutMessageBuf.append(Messages.getString("CommunicationsException.6"));timeoutMessageBuf.append(Messages.getString("CommunicationsException.7"));timeoutMessageBuf.append(Messages.getString("CommunicationsException.8"));}if (dueToTimeout == DUE_TO_TIMEOUT_TRUE || dueToTimeout == DUE_TO_TIMEOUT_MAYBE) {exceptionMessageBuf.append(lastPacketReceivedTimeMs != 0? Messages.getString("CommunicationsException.ServerPacketTimingInfo",new Object[] { Long.valueOf(timeSinceLastPacketReceivedMs), Long.valueOf(timeSinceLastPacketSentMs) }): Messages.getString("CommunicationsException.ServerPacketTimingInfoNoRecv", new Object[] { Long.valueOf(timeSinceLastPacketSentMs) }));if (timeoutMessageBuf != null) {exceptionMessageBuf.append(timeoutMessageBuf);}exceptionMessageBuf.append(Messages.getString("CommunicationsException.11"));exceptionMessageBuf.append(Messages.getString("CommunicationsException.12"));exceptionMessageBuf.append(Messages.getString("CommunicationsException.13"));} else {//// Attempt to determine the reason for the underlying exception (we can only make a best-guess here)//if (underlyingException instanceof BindException) {String localSocketAddress = propertySet.getStringProperty(PropertyKey.localSocketAddress).getValue();boolean interfaceNotAvaliable;try {interfaceNotAvaliable = localSocketAddress != null && NetworkInterface.getByName(localSocketAddress) == null;} catch (SocketException e1) {interfaceNotAvaliable = false;}exceptionMessageBuf.append(interfaceNotAvaliable ? Messages.getString("CommunicationsException.LocalSocketAddressNotAvailable"): Messages.getString("CommunicationsException.TooManyClientConnections"));}}if (exceptionMessageBuf.length() == 0) {// We haven't figured out a good reason, so copy it.exceptionMessageBuf.append(Messages.getString("CommunicationsException.20"));if (propertySet.getBooleanProperty(PropertyKey.maintainTimeStats).getValue() && !propertySet.getBooleanProperty(PropertyKey.paranoid).getValue()) {exceptionMessageBuf.append("\n\n");exceptionMessageBuf.append(lastPacketReceivedTimeMs != 0? Messages.getString("CommunicationsException.ServerPacketTimingInfo",new Object[] { Long.valueOf(timeSinceLastPacketReceivedMs), Long.valueOf(timeSinceLastPacketSentMs) }): Messages.getString("CommunicationsException.ServerPacketTimingInfoNoRecv", new Object[] { Long.valueOf(timeSinceLastPacketSentMs) }));}}return exceptionMessageBuf.toString();}

再根据上边的两个猜测,找一下对应的源码。

猜想1:autoReconnect 这个参数是有用的

网上很多说这个参数只针对mysql5以下,这个倒是没找到,官网关于这个参数的说明。
https://dev.mysql.com/doc/connectors/en/connector-j-connp-props-high-availability-and-clustering.html#cj-conn-prop_autoReconnect

如果链接断开的情况下,配置了这个参数可以发起重试。如果不配置的话,就会一直报错。

在这里插入图片描述

猜想2:HikariCP 会自动重连,tomcat-jdbc不会

可以看到 tomcat-jdbc 执行 invoke,getConnetion一气呵成,在默认配置下,也没有任何校验,如果因为第三方导致的链接断开,就麻烦了。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (compare(ISCLOSED_VAL,method)) {return Boolean.valueOf(isClosed());}if (compare(CLOSE_VAL,method)) {if (connection==null){return null; //noop for already closed.}PooledConnection poolc = this.connection;this.connection = null;pool.returnConnection(poolc);return null;} else if (compare(TOSTRING_VAL,method)) {return this.toString();} else if (compare(GETCONNECTION_VAL,method) && connection!=null) {return connection.getConnection();} else if (method.getDeclaringClass().isAssignableFrom(XAConnection.class)) {try {return method.invoke(connection.getXAConnection(),args);}catch (Throwable t) {if (t instanceof InvocationTargetException) {throw t.getCause() != null ? t.getCause() : t;} else {throw t;}}}if (isClosed()) {throw new SQLException("Connection has already been closed.");}if (compare(UNWRAP_VAL,method)) {return unwrap((Class<?>)args[0]);} else if (compare(ISWRAPPERFOR_VAL,method)) {return Boolean.valueOf(this.isWrapperFor((Class<?>)args[0]));}try {PooledConnection poolc = connection;if (poolc!=null) {return method.invoke(poolc.getConnection(),args);} else {throw new SQLException("Connection has already been closed.");}}catch (Throwable t) {if (t instanceof InvocationTargetException) {throw t.getCause() != null ? t.getCause() : t;} else {throw t;}}}

在这里插入图片描述

HikariCP

HikarPool.getConnection 会校验当前是否存活,如果不存活,会关闭当前链接,并会重新创建连接。

在这里插入图片描述

问题解决

当天的解决方案比较简单,重启服务就行。不过如果想彻底解决这个问题的话。

  • 升级连接池,tomcat-jdbc 升级 druid 或者 HikariCP ,springboot 2.0以上默认是 HikariCP

  • 修改配置,如果还使用 tomcat-jdbc :

    增加以下配置:

    # 连接存活检查
    spring.datasource.tomcat.validation-query='SELECT 1'
    # 指定多少ms执行一次连接校验.	
    spring.datasource.tomcat.validation-interval=3000
    # 指定连接校验查询的超时时间
    spring.datasource.tomcat.validation-query-timeout=-1
    
    或者连接地址增加 autoReconnect=true
    

总结

有些问题不经常遇到,即便是预发1比1的环境可能也没办法避免。除了在事故处理的时候可以完善流程,就只能尽量保持架构统一,尤其是老项目。业务侧增加全方面中间件的监控,尤其是存活检测这种。坑是无穷无尽的,踩完了记得遇到相同的跳过去。

相关文章

https://zhuanlan.zhihu.com/p/361130333?utm_id=0

https://blog.csdn.net/zhangxin09/article/details/124901850

https://blog.csdn.net/u014398624/article/details/47746473

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/662527.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sketch使用手册:从入门到精通的完整教程

Sketch软件是Mac平台上流行的矢量图形编辑软件&#xff0c;旨在帮助用户创建网站、移动应用、图标等各种设计原型。Sketch软件的设计风格简洁明了&#xff0c;界面操作简单易用&#xff0c;非常适合UI/UX设计师、平面设计师等数字创意人员。本文将根据如何使用Sketch&#xff0…

IEPE数据采集卡的作用说明

IEPE传感器是一种特殊的加速度传感器&#xff0c;其特点是自带电量放大器或电压放大器。这种传感器产生的电信号非常微弱&#xff0c;很容易受到噪声干扰&#xff0c;因此需要使用灵敏的电子器件进行放大和信号调理。为了实现更好的抗噪声性能和更方便的封装&#xff0c;IEPE传…

xmind思维导图 for mac v24.01中文版

mac电脑上思维导图软件哪个好呢&#xff1f; xmind for mac一个功能强大、易于使用的思维导图软件&#xff0c;够帮助你更好地组织思维、管理信息、规划项目和解决问题&#xff0c;提高个人和团队的工作效率。 软件下载&#xff1a;xmind思维导图 for mac v24.01中文版 XMind f…

数据可视化工具选择指南:六款主流工具的综合评测

随着大数据时代的来临&#xff0c;数据可视化已成为各行业不可或缺的工具。本文将为您介绍市面上六款主流数据可视化工具&#xff0c;包括山海鲸可视化、Echarts、D3.js、Tableau、Power BI和Funnel.io&#xff0c;帮助您更好地了解并选择适合您的工具。 山海鲸可视化 山海鲸…

【Django开发】美多商城项目第2篇:Django用户注册和登录开发(附代码,已分享)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论django商城项目相关知识。项目利用Django框架开发一套前后端不分离的商城项目&#xff08;4.0版本&#xff09;含代码和文档。功能包括前后端不分离&#xff0c;方便SEO。采用Django Jinja2模板引擎 Vue.js实现前后端…

C# .Net学习笔记—— 异步和多线程(异常处理)

一、异常处理 1、下面for循环20个线程&#xff0c;到11&#xff0c;12号的时候执行失败&#xff0c;这里我也用了try catch来捕获异常。 private void button11_Click(object sender, EventArgs e){TaskFactory taskFactory new TaskFactory();List<Task> taskList ne…

回归预测 | Matlab基于POA-LSSVM鹈鹕算法算法优化最小二乘支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于POA-LSSVM鹈鹕算法算法优化最小二乘支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于POA-LSSVM鹈鹕算法算法优化最小二乘支持向量机的数据多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab基于POA-LSSVM…

【数据结构与算法】之排序系列-20240201

【数据结构与算法】之排序系列-20240201 一、88. 合并两个有序数组二、169. 多数元素三、217. 存在重复元素四、242. 有效的字母异位词五、268. 丢失的数字六、349. 两个数组的交集七、350. 两个数组的交集 II 一、88. 合并两个有序数组 简单 给你两个按 非递减顺序 排列的整数…

【Uni-App】运行微信小程序时报错routeDone with a webviewId 2 that is not the current page

使用HBuilderX开发微信小程序&#xff0c;运行项目的时有可能会出现routeDone with a webviewId 1 that is not the current page的报错&#xff0c;但不影响运行。如果强迫症介意的话&#xff0c;可以考下面的方法进行修复。 产生原因 由于微信开发者工具的调试基础库处于灰度…

私域流量如何变现?一站式产品体系搭建与运营策略大公开

在互联网日新月异的今天&#xff0c;我们面临着人口红利逐渐消失的问题&#xff0c;各行各业的广告投放获客成本也在不断上涨。为了降低成本并提高用户转化和复购率&#xff0c;我们需要寻找新的解决方案。此时&#xff0c;私域流量的概念应运而生&#xff0c;成为了一个值得考…

【计算机二级考试C语言】C递归

目录 C 递归 数的阶乘 实例 斐波那契数列 实例 C 递归 递归指的是在函数的定义中使用函数自身的方法。 举个例子&#xff1a; 从前有座山&#xff0c;山里有座庙&#xff0c;庙里有个老和尚&#xff0c;正在给小和尚讲故事呢&#xff01;故事是什么呢&#xff1f;"从…

Python 的 pass 语句到底是什么?

Python 中的 pass 语句是一个简单的概念&#xff0c;即使没有编程经验的初学者也能很快掌握。官方文档提供了非常简单的介绍&#xff0c;下面的三个示例可以让我们快速了解如何使用它。 pass 本质上是一个空操作&#xff0c;除了允许解释器在跳过语法之前检查语法是否有效之外&…

apk反编译修改教程系列---修改apk的默认颜色 布局颜色 手机电脑同步演示【十】

往期教程&#xff1a; apk反编译修改教程系列-----修改apk应用名称 任意修改名称 签名【一】 apk反编译修改教程系列-----任意修改apk版本号 版本名 防止自动更新【二】 apk反编译修改教程系列-----修改apk中的图片 任意更换apk桌面图片【三】 apk反编译修改教程系列---简单…

PMP备考的三个阶段及学习方法分享

PMP证书是项目管理必备的关键技能证书&#xff0c;是具备进行项目管理的重要技能体现。无论升职加薪&#xff0c;还是从事项目管理工作&#xff0c;都非常重要。 个人主要从事产品开发工作&#xff0c;开始逐渐承担一些项目经理角色&#xff0c;但目前项目管理知识薄弱&#x…

深度剖析Sentinel热点规则

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 深度剖析Sentinel热点规则 前言核心概念解析&#xff1a;数字守护者的起源核心概念解析&#xff1a;简单示例演示&#xff1a; 参数索引&#xff1a;规则的基石参数索引的作用&#xff1a;不同场景下选…

sqli.labs靶场(23关到28a关)

23、第二十三关 id1单引号闭合 找位置1 and 12 union select 1,2,3 爆库&#xff1a;1 and 12 union select 1,2,database() 爆表名&#xff1a;1 and 12 union select 1,2,group_concat(table_name) from information_schema.tables where table_schemasecurity 爆字段&#…

git修改密码后mac使用sourceTree出现Authentication failed错误

1、退出sourceTree 2、在钥匙串中删除git对应站点Access Key 3、执行命令&#xff1a;git config --system --unset credential.helper 4、重新启动sourceTree&#xff0c;这时会弹出输入密码框&#xff0c;重新输入密码即可

C/C++ C++入门

个人主页&#xff1a;仍有未知等待探索-CSDN博客 专题分栏&#xff1a;C_仍有未知等待探索的博客-CSDN博客 目录 一、C关键字 二、命名空间 1、区别 1. C语言 ​编辑 2. C 2、命名空间定义 3、命名空间的使用 三、C输入&输出 四、缺省参数 五、函数重载 六、引用 …

Nijijourney V6版本动漫图像生成模型发布

简介 这是一个最先进的AI&#xff0c;可以绘制任何二次元风格的绘画&#xff01;这是一个由 Spellbrush 与 Midjourney 所共同设计开发的魔法般工具。无论您是在寻找可爱的Q版角色还是充满动感的动作场景&#xff0c;niji・journey 都能将您的想象变为现实。 功能介绍 - 增强…

第1章 简单使用 Linux

第1章 简单使用 Linux 1.1 Linux 的组成 1.2 远程连接 首先以 root 用户登录到 Linux 系统&#xff0c;然后在 Terminal 终端上输入 ip add 命令&#xff0c;来查看 IP 地址。 上图中的 192.168.72.128 就是 IP 地址。 然后打开 XShell 远程连接工具。 然后在命令提示符下输…