Redisson分布式锁有效性测试

Redis是三主三从集群模式

node:ip1:port1,ip2:port2,ip3:port3,ip4:port4,ip5:port5,ip6:port6,

spring boot 2.1.7.RELEASE ;redission版本:3.11.6

   @Bean
    public RedissonClient getRedisson(){
        Config config = new Config();
        for(String node:nodes.split(",")){
            config.useClusterServers().addNodeAddress("redis://"+node);
        }
        config.useClusterServers().setMasterConnectionPoolSize(200);
        config.useClusterServers().setSlaveConnectionMinimumIdleSize(200);
        config.useClusterServers().setMasterConnectionMinimumIdleSize(200);
        config.useClusterServers().setSlaveConnectionPoolSize(200);
        config.useClusterServers().setScanInterval(3000);
        config.useClusterServers().setPassword(password);
        return Redisson.create(config);
    }

测试加锁代码1:

@RequestMapping("/lockTest")
    public String lockTest() throws Exception {        
        RLock rl=redissonClient.getFairLock("myTestLock");
        if(rl.tryLock(5,30,TimeUnit.SECONDS)) {
            String name=Thread.currentThread().getName();
            System.out.println(name +" get lock at "+  System.currentTimeMillis());          
            return "YES";
        }
        return "NO";
    }

 

使用压测工具:Jmeter

运行35S,最终controller打印获取锁的情况是:

http-nio-9100-exec-6 get lock at 1588661240669
http-nio-9100-exec-6 get lock at 1588661240784
http-nio-9100-exec-6 get lock at 1588661240808
http-nio-9100-exec-6 get lock at 1588661240825
http-nio-9100-exec-6 get lock at 1588661242079
http-nio-9100-exec-6 get lock at 1588661242101
http-nio-9100-exec-6 get lock at 1588661242137
http-nio-9100-exec-6 get lock at 1588661242162
http-nio-9100-exec-6 get lock at 1588661242180
http-nio-9100-exec-6 get lock at 1588661242198

。。。。。

表面上看:锁的有效期是30S,为啥压测35s获取到锁是N次?

首先分清楚问题:谁持有锁?什么情况能再次获取到锁?

问题关系到的对象有:客户端请求,tomcat线程池,Redission客户端,Redis集群服务器。

参考资料:https://mp.weixin.qq.com/s/y_Uw3P2Ll7wvk_j5Fdlusw

Redission客户端会绑定锁到线程ID,同一线程如果是锁的持有者,执行加锁的时候会增加加锁次数,以及重新延长锁的有效时间,watchdog根据redisson客户端配置的scanInterval定时检测锁的有效期。

理解以上的原理,就很好解释了,tomcat线程池里面的线程是tomcat根据自己的算法分配到每一个请求,线程如果是未执行完的状态是不会分配给下一个请求的,如果已执行完并且是第一个锁的持有者的线程分配到该请求,那么该请求一定能够获取到锁。

上述示例代码的加锁后执行的代码时间极短,无论压测持续多久,获取到锁的线程ID始终是同一个:第一个获取到锁的线程。

只要是请求分配到了第一个获取到锁的线程,那么就能获取到锁,而且每次获取到锁之后,锁的有效期都会重新刷新为30S.

 

生产问题代码:

public void lockDemo(){

    RLock lock=redissionClient.getLock("XXX");

   if(!lock.tryLock(5, 300, TimeUnit.SECONDS)){
                result.put("errorCode", "ERR01");
                result.put("errorMsg", "请勿重复操作");
                return result;
    }    

    查询业务结果是否已处理,已处理则抛出异常

    final RLock lock1=lock;

    taskExecutor.execute(new Runnable(){

          final RLock lockF = lock1;

           执行主干业务。

           lockF .unlock();

   }

}

 

在测试环境单机模式下,一个人开多个浏览器窗口,同时点击,业务会偶尔出现重复处理现象,而且处理完毕后,unlock处理后锁依然未释放。

根据上述测试得出的结论:

1.主干加锁操作很快执行完毕,主干加锁业务是在主线程完成的,业务处理却是在异步的线程完成,

Redission要求加锁和解锁操作必须在同一个线程内完成,两者线程ID不相等,所以导致锁始终无法释放,只能等待锁的有效期过期。

2.由于主线程执行时间很快,第一次获取到锁的线程A在下一个同样的请求到达之后,由于线程A是已完结状态,Redission认为业务也已完结,只要线程A在主干的异步线程尚未完成处理时,又被分配给了下一个请求,tryLock是可以成功的。测试环境并发不大,而且测试环境是单机模式,所以出现的概率也会很高。

 

解决方案:1.如果不会引起客户端超时的情况下,将异步改为同步。

                   2.如果会引起客户端超时,业务处理复杂,加锁和业务处理都异步执行,客户端生成UUID,客户端不断轮训此UUID来查询业务处理结果状态。每个客户端重复处理同一个业务,但是请求ID不一样,都可以查到自己的处理结果。

 

测试案例2:

@RequestMapping("/lockTest")
    public String lockTest() throws Exception {        
        RLock rl=redissonClient.getFairLock("XXH122232");
        if(rl.tryLock(1,30,TimeUnit.SECONDS)) {
            String name=Thread.currentThread().getName();
            System.out.println(name +" get lock at "+  System.currentTimeMillis());
            Thread.currentThread().sleep(20000);
            return "YES";
        }
        return "NO";
    }

Jmeter持续压测时间<20s,本地启动多个APP应用,测试应用集群模式+Ression集群模式下锁的有效性,在原有的基础上增加一个http Request。9000端口应用和9100端口应用连同一个redis集群。

 

压测结果:

http-nio-9100-exec-1 get lock at 1588663319488

只有其中一个APP应用获取到锁,另外的一个应用没有任何打印。

验证结果:锁在应用APP集群模式+Redis集群模式下是有效的。

 

 

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值