首页 > 其他分享 >SpringBoot Seata 死锁问题排查

SpringBoot Seata 死锁问题排查

时间:2023-12-06 18:14:03浏览次数:45  
标签:SpringBoot sun Dcom 死锁 线程 jmxremote Seata

现象描述: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,sun,Dcom,死锁,线程,jmxremote,Seata
From: https://www.cnblogs.com/cjsblog/p/17878583.html

相关文章

  • springbootlearn01 ERROR汇总
    (1)关于IDEA未生成pom.xml文件通过springinitialize要确认[type]选项中为Mavenproject类型,不可选择其他(没有pom.xml)而POM(只会生成pom.xml)(2)关于spring-boot-starter-parent报红的解决方法关于mavenreload后仍无法加载包通过mvn-Uidea:idea命令重新加载maven包,具体操作是......
  • 小白使用springboot项目进行开发系统前期工作
    首先进行项目的创建,springboot项目创建有两种方式。可以使用idea直接创建springboot项目,可以使用阿里云的源,创建会快一点这里可以选择各种的依赖第二种就是直接创建maven项目,直接导入pom坐标类似于这种,点击刷新即可点击查看代码<dependency>......
  • SpringBoot项目中集成自定义公共Maven依赖如何集成与调试
    场景Nexus-在项目中使用Maven私服,Deploy到私服、上传第三方jar包、在项目中使用私服jar包:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/101391279Maven项目在pom文件中引入lib下的第三方jar包并打包进去:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/det......
  • java springboot 读取自定义配置文件
    javaspringboot读取自定义配置文件application.properties、test.properties maven中引用:<dependency><groupId>org.apache.commons</groupId><artifactId>commons-configuration2</artifactId><version......
  • 【转】SpringBoot实现策略模式
    文章来源:微信公众号《Java极客技术》 ,作者:鸭血粉丝Tang  1.普通代码实现在介绍SpringBoot中如何实现策略设计模式之前,我们先简单的回顾一下策略模式的设计思路。以编写一个简单的程序计算器,代码如下!首先,我们定义一个Operation接口,用于逻辑的计算publicint......
  • 使用分布式事务 Seata 的 TCC 模式
    Seata的TCC模式需要通过人工编码来实现数据的回滚恢复,有点麻烦,但是性能最高。TCC是3个方法的首字母缩写,即Try方法、Confirm方法、Cancel方法。Try方法进行资源的检查和冻结,Confirm方法是当所有事务都成功后调用的方法,Cancel方法是当整体事务中某个分支事务失败时调用......
  • Seata 分布式事务
    Seata分布式事务​#Seata中间件#​Seata是2019年1月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。官网地址:http://seata.io/CAP定理和Base理论这两个在前面弄Nacos的时候已经说过......
  • sping_boot学习系列-搭建springboot项目工程
    搭建springboot工程方式一.通过idea SpringInitializr搭建详细步骤:1.创建一个新项目File->New->Project...2.项目环境配置选择SpringInitializr(20231205:注最低版本是jdk17,若搭建jdk8版本的,可先搭建jdk17版本的,修改pom.xml文件)默认即可,可修改项目名称选择maven......
  • springboot @PostConstruct无效的解决
    springboot@PostConstruct无效的解决问题描述:在使用SpringBoot框架时,通过@PostConstruct注解修饰的方法可能会出现无法执行的情况。解决过程:1.确认依赖首先,我们需要确认在项目中是否引入了正确的依赖。在pom.xml文件中,需要引入spring-boot-starter-web包,这个包中含......
  • springboot整合redis
    spring:redis:port:6379host:192.168.88.101password:111111lettuce:pool:max-active:8max-wait:-1msdatabase:0yml配置(单机版)spring:redis:password:111111lettuce:cluster:refr......