1、异常描述
近期做了一个功能模块的限流熔断处理,使用的是hystrix来做熔断处理。具体的配置如下:
@DefaultProperties(defaultFallback = "customFallBackMethod",commandProperties = {@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器,默认true@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "100"), //时间窗口时间内最大并发请求次数,默认20次@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "2000"), //时间窗口时长,默认10s@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "5000"), //熔断后恢复间隔时间范围,默认5s@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "0"),//失败率达到多少后跳闸,默认50})
在需要熔断的接口上,加了@HystrixCommand注解;在启动类上也加上了@EnableHystrix注解,如此一来熔断配置就会生效啦。
配置信息就是这样啦,主要想实现的效果就是,在指定时间周期内,并发请求达到多少,就熔断处理。例如我这里写的,2秒内如果并发请求超过100次,就进入熔断降级的方法customFallBackMethod中,5秒后恢复。失败率是0,即只要并发数据量超过配置,就熔断。
坑就坑在:其中有一个被调用的接口,没有抛出异常,也没有返回空值,也没有执行很耗时间,但是基本上调用都会被熔断,从而进入降级的方法中,导致功能无法正常使用,简直莫名其妙,还刚好在上线发版时出现了,一头雾水~~~,没有办法只好先撤掉Hystrix的熔断配置,恢复功能使用。
2、分析这个坑
后来在本地启动服务,想验证一下接口的具体情况,果然复现了,同样的配置,这个接口依然基本都是熔断降级响应。通过打断点,我发现在如下这个方法执行时,
会进入到一个异常抛出的程序:
具体报错信息如下:
### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: interrupt
### The error may exist in ***/mapper/xxxMapper.java (best guess)
### The error may involve ***.mapper.xxxMapper.selectOne
### The error occurred while executing a query
### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: interrupt
这里会发现有一个数据库的连接异常,所以导致了hystrix的熔断处理,进入到了降级方法中。
但是为什么会出现这个异常呢,通过了解hystrix的执行原理,其执行流程大概如下:
所以其实是hystrix创建的新线程在执行sql前,通过mybatis-plus去获取JDBC的连接超时失败了,导致了这个异常的产生,从而进入了熔断。
那这里再去具体分析一下,为啥获取JDBC的连接时会失败呢,主要原因其实就是执行超时了。超过了hystrix的执行响应时长。
3、解决这个坑
目前有2种解决方式:
第一种,将hystrix的线程执行响应时长延长。由于我本身没有设置这个配置项:
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"
所以,默认响应超时时间为1秒,因此我们把时长增加到一个适当的时间,即可以解决。例如我们设置为3000毫秒。
第二种,在接口的执行方法上,加上@Transactional注解,使mybatis-plus在获取jdbc连接时,可以直接使用spring的事务上下文快速获取到JDBC连接,从而不至于导致超时产生异常。但是,保险起见,还是需要在第一种方式下,增加一下合理的超时时限,这样才能更好的保障程序的正常运行。