1.事故背景

在APP访问服务器接口时需要从redis中获取token进行校验,服务器上线后发现一开始可以正常访问,但只要短时间内请求量增长服务则无法响应

2.排查流程

(1)使用top指令查看CPU资源占用还远远达不到瓶颈,排查因为CPU资源不足导致服务不可用的可能

(2)查看tomcat线程池配置,默认最大线程数为200,理论上可以支持目前服务器的访问量

(3)使用jmap指令保存堆栈信息,jmap -dump:format=b,file=dump.log pid,pid为进程号

(4)使用Java visualVM打开保存的堆栈日志dump.log,发现绝大部分的线程都阻塞在从redis连接池中获取连接的代码,如下图所示

 3.原理分析

根据堆栈日志所显示得知线程访问redis前需要从连接池队列中推出一个连接,当连接池没有连接时,则会阻塞等待,阻塞等待的时间可以自行设置MAA_WAIT参数,默认是-1表示不限时等待,目前项目使用默认配置,所以所有的线程都会一直阻塞在获取连接的步骤,如果设置了最大等待时间,当超过最大等待时间会报出Could not get a resource from the pool的异常

(1)在spring配置文件中的stringRedisTemplate对象配置参数中打开了事务支持,而redis的事务支持是用MUTI和EXEC指令来支持,以下事务实例截图来自菜鸟教程 https://www.runoob.com/redis/redis-transactions.html

(2)如果要保证在事务能正常执行,那么在一个方法中多次操作redis必须是同一条连接,这样才能保证事务能正常执行,所以在stringRedisTemplate会将连接绑定在当前线程,当第二次访问redis时直接从当前线程中获取连接,绑定连接源码如下:

(3)按照流程,先绑定连接,最后在finally代码块中释放连接,看起来并没有问题,但跳进去releaseConnection方法的代码发现连接需要在事务提交后才能释放,也就是说service方法上必须使用@Transation注解修饰,但因为业务方法上少写了@Transation注解导致连接将一直绑定第一次获取他的线程上,当线程池的线程被获取完之后,其他线程就会就如阻塞等待状态,导致服务不可用

(4)如果加上@Transation注解,那么方法执行完之后将会执行TransactionSynchronizationUtils.invokeAfterCompletion这个方法,mysql事务也是在这个方法执行commit操作,如下图所示方法的第一个参数是List<TransactionSynchronization> synchronizations,代表可以有多个事务,redis,mysql等,都会此进行事务提交操作,这里使用多态,根据对象的具体类型执行不同的方法,redis则执行redis的事务提交操作,mysql则执行mysql的事务提交操作

(5)以下为redis事务提交的代码,也跟我们上面提到的一样,发送exec指令提交事务

 4.如何修改代码

(1)确认实际需求是否需要事务支持,如果需要则在对应方法上加上@Transaction注解

(2)如果不需要事务支持则将enableTransactionSupport设置为false

Spring Redis开启事务支持错误用法导致服务不可用的更多相关文章

  1. $Django python中使用redis, django中使用(封装了),redis开启事务(管道)

    一 Python操作Redis之普通连接 #先安装 pip3 install redis import redis r = redis.Redis(host='127.0.0.1', port=637 ...

  2. 修改ip导致服务不可用

    修改ip导致服务不可用 1.修改hostsvi /etc/hosts 修改ip地址 2.lsnrctl start 后会发现The listener supports no services,解决方案 ...

  3. springboot开启事务支持时报代理错误

    问题:The bean 'xxx' could not be injected as a 'com.github.service.xx' because it is a JDK dynamic pro ...

  4. 对Spring 容器管理事务支持的总结

    1.问题 Connection conn = DataSourceUtils.getConnection(); //开启事务 conn.setAutoCommit(false); try { Obje ...

  5. spring boot开启事务管理,使用事务的回滚机制,使两条插入语句一致

    spring boot 事务管理,使用事务的回滚机制 1:配置事务管理 在springboot 启动类中添加 @EnableTransactionManagement //开启事务管理 @Enable ...

  6. 如何在Spring Boot开启事务

    说到事务,那什么是事务呢? 事务(Transaction),一般是指要做的或所做的事情. 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行. ...

  7. 集群FULL GC导致服务不可用

    FULL GC会导致stop-the-world,频繁的FULL GC会影响系统的可用性.stackoverflow上有个提问是这么描述这个问题的:当服务器集群批量启动后,执行FULL GC的频率和时 ...

  8. Spring Boot—13事务支持

    pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  9. IIS应用程序池"启用32位"导致服务不可用的503错误

    原来运行正常的站点,突然不正常了,出现503错误.查看操作系统的日志查看器显示: 由于配置问题,无法加载模块 DLL“C:\Program Files (x86)\IIS\Asp.Net Core M ...

随机推荐

  1. Rocket - debug - TLDebugModuleInner - Abstract Data

    https://mp.weixin.qq.com/s/DOLkEi-_qQt6lWOhJ2hxVQ 简单介绍TLDebugModuleInner中抽象数据寄存器的实现. 1. abstractData ...

  2. jchdl - GSL Node

    https://mp.weixin.qq.com/s/Oa4qgjIUccu5Y-Jlqcyn_A   org.jchdl.model.gsl.core.meta.Node.java   ​​ gen ...

  3. Java实现 LeetCode 738 单调递增的数字(暴力)

    738. 单调递增的数字 给定一个非负整数 N,找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增. (当且仅当每个相邻位数上的数字 x 和 y 满足 x <= ...

  4. Java实现 LeetCode 138 复制带随机指针的链表

    138. 复制带随机指针的链表 给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点. 要求返回这个链表的 深拷贝. 我们用一个由 n 个节点组成的链表来表示输入/ ...

  5. java实现第四届蓝桥杯剪格子

    剪格子 题目描述 如图p1.jpg所示,3 x 3 的格子中填写了一些整数. 我们沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是60. 本题的要求就是请你编程判定:对给定的m x n 的格子 ...

  6. CentOS 6.X 双网卡绑定配置

    相关环境 主机:Dell PowerEdge R720服务器(背板有4个GE网口) 操作系统:CentOS(RHEL)6.X 网线连接 使用6类网线 将Dell R720 GE网口 0,与交换机A相连 ...

  7. monkey命令的基本使用

    看到monkey,你想到了什么?今天给大家分享下monkey命令的基本使用 monkey测试是Android平台自动化测试的一种手段,通过monkey程序模拟用户触摸屏幕,滑动.按键操作等操作对设备上 ...

  8. js实现初始化调用摄像头

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"% ...

  9. ubuntu12.04可用源

    最近试了不少源,都无法用.这一份是目前可以正常使用的 #deb cdrom:[Ubuntu 12.04.5 LTS _Precise Pangolin_ - Release amd64 (201408 ...

  10. 讨论session共享方案设计

    默认情况下,php的session文件是保存在磁盘文件中. 在php.ini配置文件中的配置项如下: session.save_handler = files session.save_path = ...