事务没有回滚,TM报redis异常
- [x] I have searched the issues of this repository and believe that this is not a duplicate.
1. Bug Description
只要出发分布式事务,TM就报redis异常:

我的TM配置:
spring:
profiles:
active: dev
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/tx-manager?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull
username: admin
password: 123456
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
hibernate.ddl-auto: update
redis:
password:
timeout: 10000ms
cluster:
max-redirects: 3 # 获取失败 最大重定向次数
nodes:
- 127.0.0.1:6380
- 127.0.0.1:6381
- 127.0.0.1:6382
lettuce:
pool:
max-active: 1000 #连接池最大连接数(使用负值表示没有限制)
max-idle: 20 # 连接池中的最大空闲连接
min-idle: 5 # 连接池中的最小空闲连接
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
tx-lcn:
logger:
enabled: true
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3306/tx-manager?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull
username: admin
password: 123456
manager:
admin-key: admin
port: 9119
dtx-time: 60000
heart-time: 5000
2. Environment:
- JDK version: 1.8
- OS: win10
- TX-LCN version: last
- Others:
3. Exception Stacktrace
org.springframework.data.redis.RedisSystemException: Unknown redis exception; nested exception is java.lang.ClassCastException: com.sun.proxy.$Proxy141 cannot be cast to io.lettuce.core.api.sync.RedisCommands
at org.springframework.data.redis.FallbackExceptionTranslationStrategy.getFallback(FallbackExceptionTranslationStrategy.java:53)
at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:43)
at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:268)
at org.springframework.data.redis.connection.lettuce.LettuceConnection.discard(LettuceConnection.java:623)
at org.springframework.data.redis.connection.DefaultStringRedisConnection.discard(DefaultStringRedisConnection.java:273)
at org.springframework.data.redis.core.RedisTemplate.lambda$discard$22(RedisTemplate.java:1019)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)
at org.springframework.data.redis.core.RedisTemplate.discard(RedisTemplate.java:1018)
at com.codingapi.txlcn.tm.core.storage.redis.RedisStorage.refreshMachines(RedisStorage.java:269)
at com.codingapi.txlcn.tm.support.service.impl.ManagerServiceImpl.refreshMachines(ManagerServiceImpl.java:86)
at com.codingapi.txlcn.tm.txmsg.EnsureIdGenEngine.onTmReceivedHeart(EnsureIdGenEngine.java:61)
at com.codingapi.txlcn.txmsg.netty.handler.RpcCmdDecoder.channelRead0(RpcCmdDecoder.java:61)
at com.codingapi.txlcn.txmsg.netty.handler.RpcCmdDecoder.channelRead0(RpcCmdDecoder.java:40)
at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:345)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:337)
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:345)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:337)
at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:345)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:337)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:323)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:297)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:345)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:337)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1408)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:345)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:677)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:612)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:529)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:491)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:905)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassCastException: com.sun.proxy.$Proxy141 cannot be cast to io.lettuce.core.api.sync.RedisCommands
at org.springframework.data.redis.connection.lettuce.LettuceConnection.getDedicatedRedisCommands(LettuceConnection.java:946)
at org.springframework.data.redis.connection.lettuce.LettuceConnection.discard(LettuceConnection.java:621)
... 39 more
4. Tour Idea
need help!!
昨天修复了下,原因是你应该和我一样用的redis集群,而redis集群是不支持事物的,它只能支持单节点事物
昨天修复了下,原因是你应该和我一样用的redis集群,而redis集群是不支持事物的,它只能支持单节点事物
3ks!
请问下如何修复的呢,是修改redis配置改成单节点了吗
覆盖源码,那一部分不用事物直接设置值进去,不必强求原子性。不过项目我最终启用了lcn,目前版本存在很大问题,比如说通知TC出错,部分回滚提交问题。反正是连redis集群支持都有问题的版本我是不敢用的
我是重写com.codingapi.txlcn.tm.core.storage.redis.RedisStorage,注释掉事务相关的代码,加啥事务, public void refreshMachines(long timeout, long... machines) { try { /stringRedisTemplate.setEnableTransactionSupport(true); stringRedisTemplate.multi();/ log.info("refreshMachines....."); for (long mac : machines) { stringRedisTemplate.opsForValue().set(REDIS_MACHINE_ID_MAP_PREFIX + mac, "", timeout, TimeUnit.MILLISECONDS); } //stringRedisTemplate.exec(); } catch (Throwable e) { // stringRedisTemplate.discard(); } finally { // stringRedisTemplate.setEnableTransactionSupport(false); } }
这样合适吗
我觉着没有必要加事务
我看代码是在EnsureIdGenEngine中调用的,看代码没有看懂,您知道这个是干嘛用的吗,redis为什么要存储tm.machine.id.gen
RedisStorage也是在该类中调用的
TM心跳检测的时候,会调用到 RedisStorage的refreshMachines,应该为了剔除不活动的TM,但是refreshMachines方法中使用了事务相关的代码,reids-cluster不支持事务操作,所以报了上面的错误,com.sun.proxy.$Proxy141 cannot be cast to io.lettuce.core.api.sync.RedisCommands
既然是为了删除不活动 的tm,直接del就可以了,没道理使用事务啊
TM心跳检测的时候,会调用到 RedisStorage的refreshMachines,应该为了剔除不活动的TM,但是refreshMachines方法中使用了事务相关的代码,reids-cluster不支持事务操作,所以报了上面的错误,com.sun.proxy.$Proxy141 cannot be cast to io.lettuce.core.api.sync.RedisCommands
这样操作,并发的时候没有问题吗
TM心跳检测的时候,会调用到 RedisStorage的refreshMachines,应该为了剔除不活动的TM,但是refreshMachines方法中使用了事务相关的代码,reids-cluster不支持事务操作,所以报了上面的错误,com.sun.proxy.$Proxy141 cannot be cast to io.lettuce.core.api.sync.RedisCommands
这样操作,并发的时候没有问题吗
我觉得没问题的,你看源码,它也只是把机器设置到redis中去,要么都成功要么都失败。那为什么会失败,大概率原因还不是其他TM修改了这些key导致的?所以就算是部分成功那又怎样呢。说到并发问题,如果你仔细看下源码,发现问题还是很多的。所以如果你想应用到线上环境,我劝你慎重考虑,毕竟现在版本问题不少,也没有重量级背书者维护。