redis timeout 单位_通过Redis 实现分布式锁_利用Jedis 客户端

精品推荐

  • 国内稀缺优秀Java全栈课程-Vue+SpringBoot通讯录系统全新发布!

  • Docker快速手上视频教程(无废话版)【免费】

  • 基于Docker实现nginx+keepalived实现web高可用web系统集群视频教程(无废话版)【免费】

作者:Uzhizhe

转载自:

https://www.cnblogs.com/liqingjiang/p/12059340.html

前言

分布式锁一般有三种实现方式:

  1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。

本篇博客将介绍第二种方式,基于Redis实现分布式锁。

虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁。

可靠性

首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

互斥性:在任意时刻,只有一个客户端能持有锁。不会发生死锁:即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。具有容错性:只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。解铃还须系铃人:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

代码实现

一、引入redis 依赖
<dependency>    <groupId>redis.clientsgroupId>    <artifactId>jedisartifactId>    <version>2.9.0version>dependency>
备注:根据版本不同,jedis 的set 方法也有所不同
二、配置文件增加redis 配置
# redis.properties 配置文件:

# region Redis jedis
# redis配置开始

# Redis数据库索引(默认为0)
spring.redis.database=0

# Redis服务器地址
spring.redis.host=localhost

# Redis服务器连接端口
spring.redis.port=6379

# Redis服务器连接密码(默认为空)
spring.redis.password=redis123456

# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=1024

# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=10000

# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=100

# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=5

# 连接超时时间(毫秒)
spring.redis.timeout=10000

# 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时
spring.redis.block-when-exhausted=true

# endregion
三、利用Spring 把JedisPool 加入Bean 工厂
@Configuration@PropertySource("classpath:application.properties")@Slf4jpublic class RedisConfig {    @Value("${spring.redis.host}")    private String host;    @Value("${spring.redis.port}")    private int port;    @Value("${spring.redis.timeout}")    private int timeout;    @Value("${spring.redis.jedis.pool.max-idle}")    private int maxIdle;    @Value("${spring.redis.jedis.pool.max-wait}")    private long maxWaitMillis;    @Value("${spring.redis.password}")    private String password;    @Value("${spring.redis.block-when-exhausted}")    private boolean blockWhenExhausted;    @Bean    public JedisPool redisPoolFactory() throws Exception {        log.info("JedisPool注入开始!!");        log.info("redis地址:" + host + ":" + port);        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();        jedisPoolConfig.setMaxIdle(maxIdle);        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);        // 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true        jedisPoolConfig.setBlockWhenExhausted(blockWhenExhausted);        // 是否启用pool的jmx管理功能, 默认true        jedisPoolConfig.setJmxEnabled(true);        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);        log.info("JedisPool注入成功!!");        return jedisPool;    }}
四、封装RedisService 对外提供服务
@Service@Slf4jpublic class RedisService {    //Redis 成功返回结果标识    private static final String LOCK_SUCCESS = "OK";    //Reis 操作返回成功    private static final Long RELEASE_SUCCESS = 1L;    //Redis锁不存在时才设置成功    private static final String SET_IF_NOT_EXIST = "NX";    //Redis锁超时时间单位    private static final String SET_WITH_EXPIRE_TIME = "PX";    @Autowired    private JedisPool jedisPool;    /**     * 尝试获取分布式锁     *     * @param lockKey    锁     * @param requestId  请求唯一标识(可以通过uuid等方式获取唯一ID)     * @param expireTime 超期时间(毫秒)     * @return 是否获取成功     */    public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {        Jedis jedis = jedisPool.getResource();                //Jedis 3.0.0 以前的版本        //String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);                //Jedis 3.0.0 及以后的版本        SetParams setParams = SetParams.setParams().nx().px(expireTime);        String result = jedis.set(lockKey, requestId, setParams);        return LOCK_SUCCESS.equals(result);    }    /**     * 释放分布式锁     *     * @param lockKey   锁     * @param requestId 请求唯一标识(加锁时用的唯一标识)     * @return 是否释放成功     */    public boolean releaseDistributedLock(String lockKey, String requestId) {        Jedis jedis = jedisPool.getResource();        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));        return RELEASE_SUCCESS.equals(result);    }}
五、简单解释
1. 利用redis 的set 命令的 5个参数保证操作的原子性

2. 利用Lua 脚本保证在释放锁时的原子性

3. 利用requestId 唯一标识保证不会释放别人的锁

长按关注锋哥微信公众号,非常感谢;

6ca09a2c733a658b1da0c8a9e3240ab6.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值