通过Redis实现分布式锁 代码分享。

Redis实现分布式锁的作用和意义,可以参考这篇文章:https://zhuanlan.zhihu.com/p/268290754

代码的话,不废话,直接看我在项目中用的。
一.配置RedisTemplate对象
我是基于RedisTemplate对象来实现分布式锁的,而不是jedis。

package com.wuyouhu.common.redis.configure;

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * redis配置
 * 
 * @author lele
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{
    
    
    @Bean
    @SuppressWarnings(value = {
    
     "unchecked", "rawtypes", "deprecation" })
    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.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);

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

二.redis分布式锁Entity类

package com.wuyouhu.common.redis.domian;

/**
 * redis分布式锁Entity类
 * 
 * @author lele
 */
public class RedisLockEntity {
    
    
    private String lockKey;
    private String requestId;

    public RedisLockEntity() {
    
    
    }

    public String getLockKey() {
    
    
        return lockKey;
    }

    public void setLockKey(String lockKey) {
    
    
        this.lockKey = lockKey;
    }

    public String getRequestId() {
    
    
        return requestId;
    }

    public void setRequestId(String requestId) {
    
    
        this.requestId = requestId;
    }

    @Override
    public String toString() {
    
    
        return "RedisLockEntity{" +
                "lockKey='" + lockKey + '\'' +
                ", requestId='" + requestId + '\'' +
                '}';
    }
}

三.lua解锁脚本代码
放在模块的resources目录下。

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

![在这里插入图片描述](https://img-blog.csdnimg.cn/af03ec8c488948fa9f5453192a9eeeea.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZCb44KS6KaL44Gk44GR44Gm,size_20,color_FFFFFF,t_70,g_se,x_16
在这里插入图片描述

四.redis分布式锁工具类

package com.wuyouhu.common.redis.service;

import java.util.*;
import java.util.concurrent.TimeUnit;

import com.wuyouhu.common.redis.domian.RedisLockEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

/**
 * spring redis 分布式锁工具类
 *
 * @author lele
 **/
@SuppressWarnings(value = {
    
     "unchecked", "rawtypes" })
@Component
public class RedisService
{
    
    
    private final static Logger logger = LoggerFactory
            .getLogger(RedisService.class);

    @Autowired
    public RedisTemplate redisTemplate;

    /**
     * 加锁,自旋重试三次
     *
     * @param redisLockEntity 锁实体
     * @return
     */
    public boolean lock(RedisLockEntity redisLockEntity) {
    
    
        boolean locked = false;
        int tryCount = 3;
        while (!locked && tryCount > 0) {
    
    
            locked = redisTemplate.opsForValue().setIfAbsent(redisLockEntity.getLockKey(), redisLockEntity.getRequestId(), 2, TimeUnit.MINUTES);
            tryCount--;
            try {
    
    
                Thread.sleep(300);
            } catch (InterruptedException e) {
    
    
                logger.error("线程被中断" + Thread.currentThread().getId(),e);
            }
        }
        return locked;
    }

    /**
     * 非原子解锁,可能解别人锁,不安全
     *
     * @param redisLockEntity
     * @return
     */
    public boolean unlock(RedisLockEntity redisLockEntity) {
    
    
        if (redisLockEntity == null || redisLockEntity.getLockKey() == null || redisLockEntity.getRequestId() == null)
            return false;
        boolean releaseLock = false;
        String requestId = (String) redisTemplate.opsForValue().get(redisLockEntity.getLockKey());
        if (redisLockEntity.getRequestId().equals(requestId)) {
    
    
            releaseLock = redisTemplate.delete(redisLockEntity.getLockKey());
        }
        return releaseLock;
    }

    /**
     * 使用lua脚本解锁,不会解除别人锁
     *
     * @param redisLockEntity
     * @return
     */
    public boolean unlockLua(RedisLockEntity redisLockEntity) {
    
    
        if (redisLockEntity == null || redisLockEntity.getLockKey() == null || redisLockEntity.getRequestId() == null)
            return false;
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript();
        //用于解锁的lua脚本位置
        redisScript.setLocation(new ClassPathResource("unlock.lua"));
        redisScript.setResultType(Long.class);
        //没有指定序列化方式,默认使用上面配置的
        Object result = redisTemplate.execute(redisScript, Arrays.asList(redisLockEntity.getLockKey()), redisLockEntity.getRequestId());
        return result.equals(Long.valueOf(1));
    }


}

五.分布式锁业务使用示例

package com.wuyouhu.templates.controller;

import com.wuyouhu.common.redis.domian.RedisLockEntity;
import com.wuyouhu.common.redis.service.RedisService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;


/**
 * 分布式锁业务
 */

@RestController
@RequestMapping("/seat")
public class SeatController extends BaseController {
    
    

    @Autowired
    private RedisService redisService;

    /**
     * 更新订单业务。。。。
     * @return
     */
    @GetMapping(value = "/testRedisLock")
    public String testRedislock1() {
    
    
        System.out.println("start....lock");
        RedisLockEntity redisLockEntity = new RedisLockEntity();
        redisLockEntity.setLockKey("lockkey");
        redisLockEntity.setRequestId("1");
        boolean lock = redisService.lock(redisLockEntity);
        if (lock){
    
    //如果上锁成功
            System.out.println("lock success!");
           	
           	// 此时只有该拿到锁(上锁)的实例可以更新订单数量......
            // 业务代码
            
       		
       		this.testRedisUnlock2()// 更新订单完成后,释放锁。
        } else {
    
     // 如果上锁不成功
            System.out.println("lock fail!");

			//此时没拿到锁,则不能更新订单数量。可以采取自旋直到抢到锁为止再去更新订单数量;或者是执行其它业务。
            // 业务代码 
        }
        System.out.println("end.....lock");
        return "success";
    }

     /**
     * 释放锁业务
     * @return
     */
    public String testRedisUnlock2() {
    
    
        System.out.println("start....unlock");
        RedisLockEntity redisLockEntity = new RedisLockEntity();
        redisLockEntity.setLockKey("lockkey");
        redisLockEntity.setRequestId("1");
            boolean unlock = redisService.unlockLua(redisLockEntity);
            if (unlock){
    
    
                System.out.println("unlock success!");
            } else {
    
    
                System.out.println("unlock fail!");
            }

        System.out.println("end....unlock");
        return "success";
    }


}

猜你喜欢

转载自blog.csdn.net/chinatopno1/article/details/121916909