分布式redis锁,spring-boot-starter-data-redis,RedisTemplate

公司聊天的聊天系统,近期出现多个客服并发接待同一个客户的记录,经排查,是由于代码加的同步锁在集群环境下不适用,

我们的客服系统是2条服务器,redis中缓存session,故考虑通过加redis分布式锁来解决该问题.

根据实际情况,只针对单台redis实例,代码逻辑先获取锁再执行操作.

代码体系用的是spring-boot-starter-data-redis,所以访问redis的客户端就是 RedisTemplate<String, String> redisTemplate

具体的加锁和解锁方法,用到了lua脚本来操作,尽可能的保证操作的原子性.

脚本封装入 DefaultRedisScript对象中,再使用RedisTemplate来执行,官方介绍

6.11. Redis Scripting

https://docs.spring.io/spring-data/redis/docs/1.6.2.RELEASE/reference/html/

lua脚本操作redis相关命令理解,请参考  http://redisdoc.com/string/index.html

1,获取锁lua脚本

redis.call('set', KEYS[1], ARGV[1],ARGV[2],ARGV[3],ARGV[4]) if redis.call('get',KEYS[1]) == ARGV[1] then return true else return false end

参数对应 set( key,reqId,NX,PX,时间毫秒)

原子操作 set NX参数,不存在key则设置key-value, PX 并同时设置key超时毫秒数,这里为了返回参数判断,加入get 获取key的值是否等于我设置的值,是则获取锁成功.

注意 expireTime 是 int类型的,RedisTemplate 底层要转字符串的,所以入参是记得加 String.valueOf(expireTime),否则会出现转换异常,当然只是我在处理得时候遇到了,特别说下.

当然这是我的参考实现,要有更好建议,还请提出.

2,释放锁lua脚本

if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) return true else return false end

参数对应 get( key) == reqId 是true则直接删除key释放锁

这个实现参考了另一篇博客,那个是用Jedis做客户端而我在RedisTemplate底层调用中没找到相似功能的方法,所以才改变思路都用lua脚本操作,后面附博客链接。

3,贴上代码如下

package com.xxxxx.kefu.utils;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;

public class RedisLockTool {

/**不存在则设置k - v 值*/
private static final String SET_IF_NOT_EXIST = "NX";
/**设置毫秒超时*/
private static final String SET_WITH_EXPIRE_TIME = "PX";

/**
* 尝试获取分布式锁
*/
public static boolean getLock(RedisTemplate<String, String> redisTemplate, String lockKey, String requestId, int expireTime) {
    String script = "redis.call('set', KEYS[1], ARGV[1],ARGV[2],ARGV[3],ARGV[4]) if redis.call('get',KEYS[1]) == ARGV[1] then return true else return false end";
    DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<Boolean>(script,Boolean.class);
    Boolean result = redisTemplate.execute(redisScript,Collections.singletonList(lockKey), requestId,SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME,String.valueOf(expireTime));
    if (result != null && result) {
         return true;
    }
    return false;
}

/**
* 释放分布式锁
*/
public static boolean unLock(RedisTemplate<String, String> redisTemplate, String lockKey, String requestId) {
         String script = "if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) return true else return false end"; 
         DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<Boolean>(script,Boolean.class);
         Boolean ok = redisTemplate.execute(redisScript,Collections.singletonList(lockKey),requestId);
         if(ok != null && ok){
               return true;
         }else{
          return false;
        }
   }
}

4,参考博客

https://yq.aliyun.com/articles/307547

里面指出了,其他实现方法的一些问题,在某些情况下不能确保操作原子性,看了理解之后,我发现同事之前用的redis锁在删除锁时没有判断客户端id,也就是里面指出的错误示例,极端情况下会出现。

参考改造出了这个实现,个人觉得是没有什么大问题的,如有发现不妥之处,还请指教.

5,查阅的其他链接

https://docs.spring.io/spring-data/redis/docs/1.6.2.RELEASE/reference/html/   RedisTemplate调用lua脚本示例

http://redisdoc.com/string/index.html   lua脚本操作redis 示例

springboot中加分布式redis锁的更多相关文章

  1. springboot中配置主从redis

    测试redis的主从配置 redis实例 文件夹名称如下 redis_master_s redis_slaver1_s redis_slaver2_s redis.conf文件 master的redi ...

  2. spring-boot 中实现标准 redis 分布式锁

    一,前言 redis 现在已经成为系统缓存的必备组件,针对缓存读取更新操作,通常我们希望当缓存过期之后能够只有一个请求去更新缓存,其它请求依然使用旧的数据.这就需要用到锁,因为应用服务多数以集群方式部 ...

  3. redis(Springboot中封装整合redis,java程序如何操作redis的5种基本数据类型)

    平常测试redis操作命令,可能用的是cmd窗口 操作redis,记录一下 java程序操作reids, 操作redis的方法 可以用Jedis ,在springboot 提供了两种 方法操作 Red ...

  4. SpringBoot中加载XML配置

    开篇 在SpringBoot中我们通常都是基于注解来开发的,实话说其实这个功能比较鸡肋,但是,SpringBoot中还是能做到的.所以用不用是一回事,会不会又是另外一回事. 涛锅锅在个人能力能掌握的范 ...

  5. springboot中如何向redis缓存中存入数据

    package com.hope;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jack ...

  6. redis分布式锁扣减库存弊端: 吞吐量低, 解决方法:使用 分段锁 分布式分段锁并发扣减库存--代码实现

    package tech.codestory.zookeeper.aalvcai.ConcurrentHashMapLock; import lombok.AllArgsConstructor; im ...

  7. [SpringBoot]SpringBoot中使用redis事务

    本文基于SpringBoot 2.X 事务在关系型数据库的开发中经常用到,其实非关系型数据库,比如redis也有对事务的支持,本文主要探讨在SpringBoot中如何使用redis事务. 事务的相关介 ...

  8. 定时任务redis锁+自定义lambda优化提取冗余代码

    功能介绍: 我系统中需要跑三个定时任务,由于是多节点部署,为了防止多个节点的定时任务重复执行.所以在定时任务执行时加个锁,抢到锁的节点才能执行定时任务,没有抢到锁的节点就不执行.从而避免了定时任务重复 ...

  9. redis分布式锁在springboot中的实现

    理论知识   redis分布式锁的实现方案请参考文章 如何优雅地用redis实现分布式锁 本案例简介   以秒杀活动为例子,在多线程高并发的情况下需要保证秒杀业务的线程安全性,确保秒杀记录与所扣库存数 ...

随机推荐

  1. javascript中间AJAX

    兼容访问XMLHttpRequest物: var xhr = null; if(window.XMLHttpRequest){ //非IE浏览器 xhr = window.XMLHttpRequest ...

  2. OpenCV For iOS 1:&#160;连接OpenCV 3.0

    本文的内容參考Instant OpenCV for iOS结合最新的开发平台完毕. 本系列文章採用的的开发环境为: 1)Xcode 6 2)OpenCV for iOS 3.0.0 alpha 接下来 ...

  3. TextView和EditText中添加图片(ImageSpan)

    编辑框中加图片,以前一直以为很复杂,后来发现android有些类已经很好的实现了这些功能. 代码如下: [java] view plaincopy mSubjectDetailView = (Text ...

  4. Facebook巴特尔与谷歌移动广告 急于打开中国市场

    随着Facebook(62.5, -0.69, -1.09%)即将设立了销售办事处在北京发酵消息.谷歌(556.33, 2.43, 0.44%)似还差点自觉保护国内市场. 6月5日,谷歌在深圳举行了面 ...

  5. 2 Task中的延续和7种阻塞

    1.wait using System; using System.Threading; using System.Threading.Tasks; namespace 多线程_List { clas ...

  6. 移动端--web开展

    近期看到群里对关于 移动端 web开发非常是感兴趣.决定写一个关于 移动端的web开发 概念或框架(宝庆对此非常是纠结).也是由于自己一直从事pc 浏览器 web一直对 移动端的不是非常重视,所以趁此 ...

  7. Leetcode 260 Single Number III 亦或

    在一个数组中找出两个不同的仅出现一次的数(其他数字出现两次) 同样用亦或来解决(参考编程之美的1.5) 先去取出总亦或值 然后分类,在最后一位出现1的数位上分类成 ans[0]和ans[1] a&am ...

  8. Beginner’s Tutorial: 3D Line and Border Effects in XAML

    This mini-tutorial might be for you if you’re having troubles finding the right line colors to achie ...

  9. kbmmw 中XML 操作入门(跨平台,而且可以与JSON,YAML,BSON 直接互相转换)

    delphi 很早以前就自带了xml 的操作,最新版里面有三种XML 解释器,一种是MSXML,看名字就知道 这个是微软自带的,这个据delphi 官方称是速度是最快的,但是只能在windows 上使 ...

  10. VS2015静态编译libcurl(C++ curl封装类)

    一.最新libcurl静态编译教程(curl-7.51版/curl-7.52版) 1.安装perl,在官网下载,安装好以后,测试perl -v是否成功 2.编译openssl(已编译好的下载地址) p ...