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

分布式 | 从 dble 日志分析到 MySQL 源码学习

作者:袁琳铸

爱可生 DBLE 团队开发成员,主要负责 DBLE 需求开发,故障排查和社区问题解答。

本文来源:原创投稿

*爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。


背景

在客户的生产环境中,dble.log 时常出现 no handler 日志。虽然没有影响客户业务的正常使用,但是需要调查下这个日志打印的原因是什么以及什么样的场景才会打印该日志。

分析

首先对 dble 和 mysql 的日志进行分析,看看能不能从日志中找到可用信息

dble日志

日志中有 no handler 日志和 dble 收到 mysql 发送的 fin 包打印的 stream closed by peer 的日志,并无其他异常。

2022-09-02 03:25:23.450  INFO [0-backendWorker] (com.actiontech.dble.net.response.DefaultResponseHandler.closeNoHandler(DefaultResponseHandler.java:116)) - no handler bind in this service MySQLResponseService[isExecuting = false attachment = null autocommitSynced = true isolationSynced = true xaStatus = 0 isDDL = false complexQuery = false] with response handler [null] with rrs = [null]  with connection BackendConnection[id = 342 host = 10.186.62.41 port = 3312 localPort = 51886 mysqlId = 677 db config = dbInstance[name=M1,disabled=false,maxCon=1000,minCon=10]
2022-09-02 03:25:23.450  INFO [0-backendWorker] (com.actiontech.dble.net.connection.BackendConnection.close(BackendConnection.java:125)) - connection id 342 mysqlId 677 close for reason no handler
2022-09-02 03:25:23.450  INFO [1-NIOBackendRW] (com.actiontech.dble.net.service.AbstractService.beforeInsertServiceTask(AbstractService.java:170)) - prepare close for conn.conn id 342,reason [stream closed by peer]
2022-09-02 03:25:23.450  INFO [0-backendWorker] (com.actiontech.dble.net.connection.AbstractConnection.closeImmediatelyInner(AbstractConnection.java:169)) - connection id close for reason [no handler] with connection BackendConnection[id = 342 host = 10.186.62.41 port = 3312 localPort = 51886 mysqlId = 677 db config = dbInstance[name=M1,disabled=false,maxCon=1000,minCon=10]

mysql 日志

存在连接超时而关闭连接的日志,并无其他异常

2022-09-02T03:25:23.375174Z 672 [Note] [MY-013730] [Server] 'wait_timeout' period of 20 seconds was exceeded for `root`@`%`. The idle time since last command was too long.
2022-09-02T03:25:23.375252Z 677 [Note] [MY-010914] [Server] Aborted connection 677 to db: 'unconnected' user: 'root' host: '10.186.62.148' (The client was disconnected by the server because of inactivity.).

dble 相关逻辑

下发语句

  • dble 收到客户端发送的语句
  • dble 从连接池中选择处理语句的连接
  • 根据语句类型 dble 选择合适的 responseHandler(responseHandler 用来- 处理 mysql 发送的报文,空闲连接的 responseHandler 为 null)
  • 下发语句到 mysql

处理 mysql 报文

  • dble 接收 mysql 发送的报文
  • dble 的 connection 使用下发语句时设置的 responseHandler 处理该报文(找到 responseHandler 的情况下这里结束)
  • dble 的 connection 无法找到 responseHandler ,无法处理该报文,打印 no handler 日志

后端连接 close 处理

  • dble 收到 fin 包或者检测到该连接存在异常
  • 打印连接 close 相关信息(close 原因,被关闭的连接的信息)
  • 清空连接绑定的内容(比如 responseHandler ,service 等信息)
  • 设置该连接不可用
  • 从连接池中移除该连接

理论情况下只有 dble 执行语句的的后端连接进行 close 处理,dble 依旧在使用这个被 close 的后端连接处理 mysql 发送的数据才会出现 no handler 日志,从日志中可以发现 dble 先处理 no handler 日志关闭连接,然后才收到了 mysql 发送的 fin 包,这与 dble 的逻辑不符(不应该先打印 no handler 日志),这是需要排查的疑惑点。然后开始翻阅 dble 的源码看看能不能用肉眼 debug 出一个符合日志打印的场景和对日志中的疑惑点进行解释,经过翻来覆去的 review 没有发现可能场景也无法解释 dble 日志中的现象。

这时候可以换个方式,借助 arthas 去客户环境中看看打印日志时 dble 的调用链和当时 dble 后端 connection 的信息,然后在尝试分析问题。

arthas 分析

  • arthas 命令
stack com.actiontech.dble.net.response.DefaultResponseHandler closeNoHandler -n 1000 >> closeNoHandler_stack.log
watch com.actiontech.dble.net.response.DefaultResponseHandler closeNoHandler '{params,returnObj,throwExp,target}' -n 1000 -x 3 -b >> closeNoHandler.log
  • 从 closeNoHandler_stack.log 可以看到 dble 收到了 mysql 传过来的 error 报文,调用了 error 方法,然后再调用 closeNohander 方法打印了 no handler 日志

  • 从 closeNoHandler.log 中可以看到是一个空闲连接收到了 mysql 发送的 error 报文

dble 中 responseHandler = null 只有两种可能1.执行 sql 语句的 connection 执行 close 后会把 responseHandler 置为 null 2.空闲连接的 responseHandler 默认为 null ,因为 connection 执行 close 会在日志中会打印 close 相关日志,而 dble 日志中没有打印所以排除可能1

结合上述日志分析,看起来是 mysql 因为连接超时关闭该连接,这时候 dble 不仅收到 mysql 发送的 fin 包还收到了 mysql 发送的 error 报文,然后 dble 处理 error 报文打印了 no handler 日志。使用和客户相同的 mysql 版本(8.0.25)按照猜想进行尝试。

实验

  • mysql 设置超时时间
mysql> set global interactive_timeout=20;
Query OK, 0 rows affected (0.00 sec)
  • 在 MySQL 服务器上开始抓包
tcpdump  port 3312 -w time_wait.cap
  • 启动 dble ,然后等待连接池中的连接超时,停止抓包
  • dble 日志中检索打印 no handler 日志的后端连接,查看 mysqlid(本实验中为667)和 localPort(本实验中为51886)
  • 拿着 mysqlid 在 mysql error log 中检索,确认为因为连接超时 mysql 主动关闭了该连接
  • 使用 tcp.port == 51886 在抓包文件中过滤信息

wireshark 分析抓包文件

从抓包内容中可以看到 mysql 的连接超时后,mysql 先向客户端(dble)发送了一个 error 报文然后在发送一个 fin 包,符合我们的猜想

这里就忍不住想看看这个抓包结果中的 Server Greeting Error 4031 是什么然后在 mysql 文档中找到了相关信息

  • Error 4031 报文详细信息

  • 发现是在 mysql8.0.24 中加入的,然后去 mysql 的 8.0.24 Release Notes 查看是否有详细说明。可以看到 Release Notes 中提到了因为 wait_timeout 闭的连接 mysql 在关闭该连接之前会先向客户端发送一个包含关闭该连接的详细原因的报文

  • 再看看具体代码处理

源码简要说明

  • pr 提交之前的逻辑(红色部分和灰色部分)
    • 直接向客户端发送 last_errno 的内容(这里向客户端发送的是 psh 包,不是 mysql 协议的报文)
    • net->last_errno 置为 ER_NET_WAIT_ERROR
    • 使用 LogErr 方法将 ER_NET_WAIT_ERROR 的内容写入 error log
    • 连接等 write 日志和发送数据结束后关闭
  • pr 提交之后的逻辑(绿色部分和灰色部分)
    • net->last_errno 置为 ER_CLIENT_INTERACTION_TIMEOUT
    • 使用 LogErr 方法将 ER_NET_WAIT_ERROR 的内容写入 error log
    • 向客户端发送 last_errno 的内容(Error 4031)
    • 连接等 write 日志和发送数据结束后关闭

结论

mysql8.0.24 及其之后的版本在因为 mysql 连接超时的情况下会向客户端先发送一个 error 报文在关闭连接 。如果 mysql 关闭的是 dble 连接池中的空闲连接,dble 的空闲连接会处理这个报文,由于空闲连接不存在 responseHandler(responseHandler 是 dble 用来处理 mysql 发送的报文,所以只有执行语句的连接才会有 responseHandler ,空闲连接默认 responseHandler 为 null),所以 dble 就直接打印了 no handler 日志,这是一个正常现象,当然对连接超时的 error 报文 dble 会在之后进行调整。

参考

https://arthas.aliyun.com/doc/

https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-24.html

https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html

https://github.com/mysql/mysql-server/commit/14508bbb1790697c28659dd051fbc855cd3b5da9

相关文章:

  • 天呐,我居然可以隔空作画了
  • 【面试】软件自动化测试岗位面试题和答案
  • 氨基功能化离子液体修饰SBA-15(NH2-IL-SBA)|含有烯丙基的离子液体氯化1-烯丙基-3-甲基咪唑(AMIMCl)
  • 【c ++ primer 笔记】第 14章 重载运算符
  • Nginx+Tomcat负载均衡、动静分离集群
  • Linux入门学习 —— 常用的基本命令(下)
  • 11、Java 变量作用域、构造方法官方教程
  • NumPy的常用函数
  • 字符串常量池与StringBuilder
  • 时间序列中的特征选择:在保持性能的同时加快预测速度
  • 谷粒商城 (九) --------- 前端基础 Vue 篇
  • MySQL锁机制详解-表锁与行锁
  • mysql行锁,表锁,间隙锁
  • 【GEE笔记11】数值Number(常见指令方法4)
  • Linux_进程概念
  • Akka系列(七):Actor持久化之Akka persistence
  • ECMAScript 6 学习之路 ( 四 ) String 字符串扩展
  • HTTP中的ETag在移动客户端的应用
  • iOS 颜色设置看我就够了
  • js操作时间(持续更新)
  • PHP CLI应用的调试原理
  • React as a UI Runtime(五、列表)
  • React+TypeScript入门
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • Transformer-XL: Unleashing the Potential of Attention Models
  • ucore操作系统实验笔记 - 重新理解中断
  • 高程读书笔记 第六章 面向对象程序设计
  • 高性能JavaScript阅读简记(三)
  • 给github项目添加CI badge
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 模型微调
  • 前端面试题总结
  • 前端知识点整理(待续)
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 在Docker Swarm上部署Apache Storm:第1部分
  • Java数据解析之JSON
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​ubuntu下安装kvm虚拟机
  • ​决定德拉瓦州地区版图的关键历史事件
  • #Linux(Source Insight安装及工程建立)
  • #数学建模# 线性规划问题的Matlab求解
  • (1)(1.9) MSP (version 4.2)
  • (173)FPGA约束:单周期时序分析或默认时序分析
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (vue)页面文件上传获取:action地址
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (强烈推荐)移动端音视频从零到上手(上)
  • (完整代码)R语言中利用SVM-RFE机器学习算法筛选关键因子
  • .java 9 找不到符号_java找不到符号
  • .NET Reactor简单使用教程
  • .NET/C# 的字符串暂存池
  • .NET/C# 使用反射调用含 ref 或 out 参数的方法
  • @Repository 注解
  • @RequestMapping-占位符映射
  • @RestControllerAdvice异常统一处理类失效原因