使用redis整合springboot实现分布式锁

使用redis整合springboot实现分布式锁:
分布式锁:在对图片进行标注的时候,为了保证数据一致性的问题,只能有一个用户进行操作标记,其他的用户等到当前用户取消对图片的操作之后才能进行访问。

整体的处理逻辑: 首先创建锁的实体MathodLock,添加用户图片等属性。
在图片控制类中将判断是否获得锁并且加到表

boolean flag = redisService.lock(String.valueOf(id),String.valueOf(userId));
            if (flag){
                MethodLock methodLock = new MethodLock();
                methodLock.setUserId(userId);
                methodLock.setMethod("pictureInfo");
                methodLock.setStatus(0);
                methodLock.setPicId(id);
                methodLockService.add(methodLock);
            }

当用户解除对图片的操作之后解除锁。

MethodLock methodLock = methodLockMapper.getState(userId,"pictureInfo",id);
            if (methodLock != null){
                int state = methodLock.getStatus();
                if (state == 0){
                    redisService.unlock("picId-" + String.valueOf(id),"userId-" + String.valueOf(userId));
                    methodLock.setStatus(1);//状态修改成解锁状态
                    methodLockService.update(methodLock);//更新方法锁的状态
                }

首先更新配置文件RediConfig

@Slf4j
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.jedis.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.database}")
    private int database;

    @Bean
    public JedisPool redisPoolFactory() {
        log.info("JedisPool注入成功!!");
        log.info("redis地址:" + host + ":" + port);
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);

        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password, database);

        return jedisPool;
    }
    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
    {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        serializer.setObjectMapper(mapper);

        template.setValueSerializer(serializer);
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

首先要继承CachingConfigurerSupport类定义自己的配置类名,在配置类中定义JedisPool线程池JedissPool是一个线程安全的网络连接池,我们可以通过JedisPool创建和管理Jedis实例
spring-data-redis针对jedis提供了如下功能:

连接池自动管理,提供了一个高度封装的“RedisTemplate”类针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口 
ValueOperations:简单K-V操作
SetOperations:set类型数据操作
ZSetOperations:zset类型数据操作
HashOperations:针对map类型的数据操作
ListOperations:针对list类型的数据操作
过JedisPool创建和管理Jedis实例

Long result = jedis.setnx(key, value);返回值result :设置成功,返回 1 。设置失败,返回 0
expire是设置redis过期时间的命令单位为秒
实现RedisService服务类

@Service
@Slf4j
public class RedisService {
//    private String lock_key = "redis_lock";//锁键

    protected long internalLockLeaseTime = 3000;

    private long timeOut = 999999;//获取锁的超时时间

    private int expireTime = 5 * 60;

//    //SET命令的参数
    SetParams params = SetParams.setParams().nx().px(internalLockLeaseTime);

//    private String NX = "NX";
//
//    private String EX = "EX";
//
//    private int expireTime = 3000;

    @Autowired
    JedisPool jedisPool;

    /**
     * 加锁
     * @param picId key
     * @param userId value
     * @return
     * */
    public boolean lock(String picId,String userId) {
        Jedis jedis = jedisPool.getResource();
        Long start = System.currentTimeMillis();
        try {
            for (;;){
                //SET命令返回ok,则证明获取锁成功
//                String lock = jedis.set("picId-" + picId, "userId-" + userId, params);
//                String key = jedis.get("picId-" + picId);
//                System.out.println(key);
//                if ("OK".equals(lock)){
//                    return true;
//                }
                //首先检查是否持锁
                if (jedis.exists("picId-" + picId) ){
                    if (jedis.get("picId-" + picId).equals("userId-" + userId)){
                        return true;
                    }else {
                        //如果不是当前用户返回持有锁,直接返回
                        return false;
                    }
                }



                //给picture持有锁
                if (jedis.setnx("picId-" + picId, "userId-" + userId) == 1){
                    jedis.expire("picId-" + picId,expireTime);
                    return true;
                }
                //否则循环等待,在timeout时间内仍未获取到锁,则获取失败
                long l = System.currentTimeMillis() - start;
                if (l >= timeOut){
                    return false;
                }
                try {
                    Thread.sleep(100);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }

        }finally {
            jedis.close();
        }
    }

    /**
     * @param picId key
     * @param userId value
     * @return 是否释放锁的boolean值
     * */
    public boolean unlock(String picId,String userId){
        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";
        try{
            Object result = jedis.eval(script, Collections.singletonList(picId),Collections.singletonList(userId));
            if ("1".equals(result.toString())){
                return true;
            }
            return false;
        }finally {
            jedis.close();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值