RedisTemplate实现的分布式锁疑似线程不安全的问题
在测试环境没有复现,上生产出现这样的问题频率好像还挺高,spring-data-redis版本1.4.2
异常如下:
java.lang.ClassCastException: java.lang.Long cannot be cast to [B
at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:181) ~[jedis-2.6.2.jar:?]
at redis.clients.jedis.Jedis.set(Jedis.java:79) ~[jedis-2.6.2.jar:?]
at com.xxx.locks.DistributedLock$1.doInRedis(DistributedLock.java:45) ~[xxx-common-1.0-SNAPSHOT.jar:?]
at com.xxx.locks.DistributedLock$1.doInRedis(DistributedLock.java:39) ~[xxx-common-1.0-SNAPSHOT.jar:?]
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:190) ~[spring-data-redis-1.4.2.RELEASE.jar:1.4.2.RELEASE]
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:152) ~[spring-data-redis-1.4.2.RELEASE.jar:1.4.2.RELEASE]
at com.xxx.locks.DistributedLock.lock(DistributedLock.java:39) [xxx-common-1.0-SNAPSHOT.jar:?]
at com.xxx.kafka.consumer.EmployeeDataConsumerListener.onMessage(EmployeeDataConsumerListener.java:70) [xxx-service-1.0-SNAPSHOT.jar:?]
at com.xxx.kafka.api.consume.KafkaConsumer$ConsumeTask.run(KafkaConsumer.java:247) [xxx.kafka-api-core-1.15.1.jar:?]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_172-ea]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_172-ea]
废话不多说这是我的实现代码:
public boolean lock(String key, String requestId) {
String lockResult = "";
try {
lockResult = (String) iRedisCache.getRedisTemplate().execute(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection redisConnection) {
Object nativeConnection = redisConnection.getNativeConnection();
String result = "";
if (nativeConnection instanceof JedisCommands) {
result = ((JedisCommands) nativeConnection).set(key, requestId, "NX", "EX",
DISTRIBUTED_LOCK_SECONDS_TIME);
}
return result;
}
}, true);
} catch (Exception e) {
log.error("{}" ,e.getMessage(), e);
}
return "OK".equals(lockResult);
}
public void releaseLock(String key, String requestId) {
try {
iRedisCache.getRedisTemplate().execute(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection redisConnection) {
Jedis jedis = (Jedis) redisConnection.getNativeConnection();
final String script = "if redis.call(\"get\",\"" + key + "\") == \"" + requestId + "\"then " +
"return redis.call(\"del\",\"" + key + "\") else return 0 end ";
Long effectRows = (Long) jedis.eval(script);
return "OK";
}
}, true);
} catch (Exception e) {
log.error("{}" ,e.getMessage(), e);
}
}
分布式锁在消费kafka时针对用户id加锁使用:
boolean getLock = distributedLock.lock(key, requestId);
while (!getLock) {
Thread.sleep(DistributedLock.THREAD_WAIT_TIME);
getLock = distributedLock.lock(key, requestId);
}
if (getLock) {
//do something
distributedLock.releaseLock(key, requestId);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
jredis的connection对象有个buffer用来缓存数据,但是当出现异常的时候,里面的buffer数据不会被清除,而是直接把这个redisConnection还给连接池,所以要手动捕获异常销毁掉这个链接,不然其他线程拿到就会报你这个错误,网上很多类似的问题,你可以搜搜看。另外redis分布式锁建议使用redisson,同时也建议redis客户端使用lettuce,别用jredis
释放锁的时候有java.Lang.String不能转Long类型的CastException