controller调用另一个controller中的方法 获取返回值_程序员一个小配置导致Redis假死,大牛架构师5分钟找到问题根源...

新版本发布上线,正想偷懒休息一下子,就收到了来自测试反馈的BUG:某个功能不能用了,让我紧急排查一下。

我看了一下代码,逻辑不是很复杂,就是调用Service,Service里进行一些业务处理,最后更新并返回Redis缓存的值,Service代码大概是这样子的(省略不相干的内容):

缓存突然不返回结果了

一番排查下来,我发现是RedisTemplate的increment方法的返回值变成了null,就像假死了一样,所以导致业务出错。

  • 难道Redis坏了?

我打开可视化工具试着连上Redis,发现缓存数据存在,所以应该不是Redis的问题

  • 难道是代码的问题?

试一下吧,我们用两种方式分别来获取一下缓存对比一下,第一次从Service方法返回值,第二次我们直接从Redis取值,代码如下:

执行一下,我们来看一下对比结果:

可以看到,从Service方法里查询Redis返回值为null,从Controller直接查询Redis则是正常返回。

同一个结果,从不同的地方查询会返回不同的值,啥情况?

原来是事务惹的祸

看来这难题我是搞不定了,只好向我的架构师老大求救。

我给老大讲了一下奇怪的现象,一开始老大也觉得奇怪,后来想了想问我有没有改动Redis的配置文件,我这才想起来确实改了Redis的一个配置:

我开启了Redis的事务支持功能

事情是这样的:那天我正好闲着没事,就翻翻代码学习学习老大架构的精髓,不知怎的就看到了Redis的配置文件,RedisTemplate里面setEnableTransactionSupport属性,我心想Redis开启事务支持不挺好的嘛,鬼使神差地就给设置开启了,回头我也没当回事直接就发版了。

我把开启事务支持的配置去掉再一试,果然没问题了。

老大告诉我,Redis跟关系型数据库不太一样,开启事务支持以后,会自动执行multi命令接下来所有的命令都会进入一个队列Queue中等待,直到调用exec命令时才会真正顺序去执行,所以代码中拿不到返回值。

源码分析

因为RedisTemplate所有的方法最后都会执行execute方法,我们首先看一下execute方法:

我发现如果开启了事务支持,则会将连接与当前线程绑定,否则就直接获取一个新连接,我们主要看一下开启事务支持的情况,进到bindConnection方法,并最终跟到doGetConnection方法:

我发现如果开启了事务支持,会调用一个potentiallyRegisterTransactionSynchronisation的方法,名字还挺长的,进去看一下:

首先判断当前是不是处于一个事务当中,也就是判断我们有没有加注解,如果处在一个事务当中就会去自动调用multi命令,所以后面的命令都会首先进入队列等待,并立即返回null,直到执行exec命令才会真正执行命令,并一次性返回所有命令的执行结果。

另外老大跟我说:不建议启用redis事务,因为Redis对于事务的支持并不是很完善,事务中遇到错误不会回滚,Redis事务只能保证ACID中的隔离性和一致性,无法保证原子性和持久性。

还好老大给力,及时发现了问题所在才没有造成太大后果,否则我估计得被经理喷的体无完肤……

我是一名正儿八经的程序员,喜欢我的文章欢迎 转发 及 关注,我也会经常与大家分享工作当中的代码那些事儿。

你可能感兴趣的:(获取返回值,redis返回的结果是null)