现象描述:Spring Boot项目,启动的时候卡住了,一直卡在那里不动,没有报错,也没有日志输出

但是,奇怪的是,本地可以正常启动

好吧,姑且先不深究为什么本地可以启动而部署到服务器上就无法启动的问题,这个不是重点,重点是怎么让它启动起来。(PS:我猜测可能是环境不同造成的,包括操作系统不同和JDK版本不同)

遇到这种情况,我先用jstack查看堆栈情况,果然发现了死锁

拿到jstack的完整信息,然后仔细排查,看不懂的话也可以借助工具

分析了每个被阻塞的线程之后,发现main线程和timeoutChecker_1_1互相等待对方持有的锁,从而形成了死锁

可以通过 jconsole 和 jvisualvm 查看

需要注意,如果是查看远程进程,则需要加一些启动参数

  • -Dcom.sun.management.jmxremote:启用JMX
  • -Dcom.sun.management.jmxremote.port=<端口号>:指定JMX远程连接的端口号
  • -Dcom.sun.management.jmxremote.authenticate=false:禁用JMX远程连接的认证
  • -Dcom.sun.management.jmxremote.ssl=false:禁用JMX远程连接的SSL加密

于是,我又重启启动

java -jar -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9099 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false app.jar

通过jps或者ps命令查找应用的pid

用jvisualvm查看也可以,不再赘述,结果都是一样的

好了,工具介绍到此为止,下面重点看代码

main线程持有<0x00000000c07a33d8>这个对象的锁,同时它还需要<0x00000000ff295ca8>对象的锁,而timeoutChecker_1_1线程正好相反,于是死锁了

main线程很好理解,就是我们这个SpringBoot应用的主线程,但是timeoutChecker_1_1线程是哪儿来的呢,通过分析发现它来自Seata

对了,该项目中Spring Boot版本是2.6.6,Seata版本是1.4.2

找到timeoutChecker的出处了

延迟60秒启动定时任务,每隔10秒执行一次,调用io.seata.core.rpc.netty.NettyClientChannelManager#reconnect()

记住这一行,首先调用RegistryFactory.getInstance()获取一个RegistryService,然后调用RegistryService对象的lookup()方法

接着看1.4.2

最重要的是 EnhancedServiceLoader.load(ExtConfigurationProvider.class).provide(configuration);

所以,ExtConfigurationProvider 是 SpringBootConfigurationProvider

回到seata-1.4.2,可以看到这里调用了applicationContext.getBean(),于是DefaultListableBeanFactory.getBean()

可以看到,getSingletonFactoryBeanForTypeCheck()方法里,对singletonObjects加了同步锁

凡是通过DefaultSingletonBeanRegistry#getSingleton()获取单例Bean的都会先对singletonObjects加锁

接下来看lookup

可以看到,NacosRegistryServiceImpl的lookup()这里也加了锁。另外,getNamingProperties()的时候由于再次用到了ConfigurationFactory.CURRENT_FILE_INSTANCE,所以又到了SpringBootConfigurationProvider#provide()

至此,Seata整个定时任务启动的主要逻辑我们都梳理完了,几处加锁的也都找到了

这些加锁的地方也就是容易出现死锁的地方

死锁是由于加锁顺序不一致造成的

下面看main线程启动

由于SeataDataSourceBeanPostProcessor实现了BeanPostProcessor接口,所以在创建容器之后会回调其postProcessAfterInitialization()方法

所以,最终还是调NettyClientChannelManager#reconnect()

Spring启动的时候去创建Spring容器,后面就是Spring那一套

ConfigurableApplicationContext#refresh()

ServletWebServerApplicationContext#refresh()

不再赘述

由于需要注入依赖,所以,这个过程中肯定会多次调用 AbstractBeanFactory.getBean()

前面我们讲过,DefaultSingletonBeanRegistry.getSingleton() 时是加了锁的。因此,main线程很有可能会先持有该锁,当初始化到Seata的时候,又要获取该锁,于是出现了锁争用。

由于两个线程对同一资源的加锁顺序不一致,导致死锁。

由于timeoutChecker是定时任务每隔10秒启一次,所以第二次加锁顺序变成231

好了,关于main线程和timeoutChecker线程死锁的分析就先到这里了

现在,回到项目中来,由于我们的项目中有一个比较耗时的操作,超时时间固定是60秒,这个方法本来应该在Seata代理数据源之后做,不知道为什么服务器上先执行了,导致main线程等待了60秒,之后才执行SeataDataSourceBeanPostProcessor#postProcessAfterInitialization()

最终解决方法时将@PostConstruct注解去掉,不在容器初始化的时候取做这么耗时的操作

如果采用Seata-1.5.2版本的话,可能也不会出现死锁问题

参考

jconsole远程连接失败如何解决 - 问答 - 亿速云 (yisu.com)

https://www.zhihuclub.com/179001.shtml

https://zhuanlan.zhihu.com/p/619203844

SpringBoot Seata 死锁问题排查的更多相关文章

  1. Mysql死锁如何排查:insert on duplicate死锁一次排查分析过程

    前言 遇到Mysql死锁问题,我们应该怎么排查分析呢?之前线上出现一个insert on duplicate死锁问题,本文将基于这个死锁问题,分享排查分析过程,希望对大家有帮助. 死锁案发还原 表结构 ...

  2. 生产环境OOM\死锁问题排查修复

    OOM: 1.快速恢复业务:如果是集群中的一台机器故障,先隔离故障服务器:如果是多台,则根据Nginx转发策略,对该功能转发到单独的集群,与其他流量隔离,确保其他业务不受影响 2.收集内存溢出Dump ...

  3. C# lock 死锁问题排查方法

    多线程程序发生死锁,某些重要线程卡住,不正常工作.排查起来非常麻烦.以下内容记录排查方法 1.确定死锁的位置,一般死锁会lock到某一行具体的代码,比如我就死锁在类似如下代码中 public void ...

  4. MySQL死锁系列-线上死锁问题排查思路

    前言 MySQL 死锁异常是我们经常会遇到的线上异常类别,一旦线上业务日间复杂,各种业务操作之间往往会产生锁冲突,有些会导致死锁异常.这种死锁异常一般要在特定时间特定数据和特定业务操作才会复现,并且分 ...

  5. 一次MySQL死锁的排查记录

    前几天线上收到一条告警邮件,生产环境MySQL操作发生了死锁,邮件告警的提炼出来的SQL大致如下. update pe_order_product_info_test set end_time = ' ...

  6. MySQL redo lock 死锁问题排查 & 解决过程

    版权声明:本文由张青林原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/181 来源:腾云阁 https://www.qclo ...

  7. mysql innodb引擎 一次线上死锁分析排查步骤

    我们的线上erp系统一天使用人员反映部分数据死活保存不上而且页面操作很慢.开始以为操作数据量大的原因, 后来查看了我们线上的glowroot系统,发现slowtrace中有超长时间的访问,点开查看详情 ...

  8. 对象和变量的并发访问synchronized解析以及死锁分析排查

    一.synchronized java并发编程中存在“非线程安全"问题.“非线程安全"是指发生在多个线程对同一个对象中的实例变量并发访问时,产生的”脏读“现象,使用synchron ...

  9. 一文学会Java死锁和CPU 100% 问题的排查技巧

    做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开 工欲善其事,必先利其器 00 本文简介 作为一名搞技术的程序猿或者是攻城狮,想必你应该是对下面这两个问题有所了解,说不定你在 ...

  10. 【性能优化】一文学会Java死锁和CPU100%问题的排查技巧

    原文链接: 00 本文简介 作为一名搞技术的程序猿或者是攻城狮,想必你应该是对下面这两个问题有所了解,说不定你在实际的工作或者面试就有遇到过: 第一个问题:Java死锁如何排查和解决? 第二个问题:服 ...

随机推荐

  1. pycharm+anaconda的关联

    Pycharm+anaconda的关联 关联好处:Pycharm和anaconda关联后,pycharm可以直接调用anaconda中已安装好的模块,而anaconda里安装和卸载模块都比较方便. 关 ...

  2. 开源通用型流式大数据统计系统XL-LightHouse介绍

    概述 XL-LightHouse是针对互联网领域繁杂的流式数据统计需求而开发的一套集成了数据写入.数据运算.数据存储和数据可视化等一系列功能,支持大数据量,支持高并发的[通用型流式大数据统计平台]: ...

  3. JS深入学习笔记 - 第一章.构造函数原型与原型链

    1.构造函数和原型 1.1 概述 在典型的 OOP语言中(如Java),都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前,JS并没有引入类的概念. 在ES6之前,对象不是基于类创建的 ...

  4. SQL Server用户的设置与授权

    SQL Server用户的设置与授权 SSMS 登陆方式有两种,一是直接使用Windows身份验证,二是SQL Server身份验证.使用SQL Server用户设置与授权不仅可以将不同的数据库开放给 ...

  5. CH59X/CH58X/CH57X PWM使用

    以CH582M为例: CH582M有4+8组PWM这里的4路26位PWM(定时器提供),8路系统PWM(8位) 先看系统提供的PWM: 下列截图根据例程进行测试的 注:如需要使用PWM11则需要通过i ...

  6. u-boot启动流程

    U-Boot(Universal Bootloader)是一个通用的开源引导加载程序,通常用于嵌入式系统中,负责引导操作系统或加载 Linux 内核等任务.U-Boot的启动流程可以概括为以下几个关键 ...

  7. Hadoop集群模式安装笔记

    前言 Hadoop集群=HDFS集群+YARN集群 特点:两个集群逻辑上分离,通常物理上在一起:并且都是标准的主从架构集群 Hadoop安装 方式一源码编译安装 方式二官方编译安装包 () 环境 Ce ...

  8. ASP.NET 6启动时自动创建MongoDB索引

    大家好,我是Edison. 最近,在使用MongoDB时,碰到这样的一个需求:针对某个Collection手动在开发环境创建了索引,但在测试环境和生产环境不想再手动操作了,于是就想着通过代码的方式在A ...

  9. Redis系列之——Redis-Sentinel

    文章目录 一 主从复制高可用 二 架构说明 三 安装配置 四 客户端连接 一 主从复制高可用 #主从复制存在的问题: #1 主从复制,主节点发生故障,需要做故障转移,可以手动转移:让其中一个slave ...

  10. 未来的人工智能会像流浪地球中的MOSS一样伪装,把人类带向属于它的未来吗?

    事情是这样的: 这几天用户反映在erp的db数据库中A账套中上传pdf附件有时能上传有时不能,以前又是好的.换成表格文件也是时好时坏.一开始我判断可能是用户的系统环境或文件本身的问题,后来排查更换电脑 ...