基于redisTemplate的redis的分布式锁正确打开方式

首先给大家推荐一下我老师大神的人工智能教学网站。教学不仅零基础,通俗易懂,而且非常风趣幽默,还时不时有内涵黄段子!点这里可以跳转到网站

      最近在做项目中要用到redis锁,因为项目是集群部署,所以要考虑分布式锁,查了一些资料,发现有些会产生误导,所以还是记录一下redis分布式锁的正确打开方式,话不多说,先上代码

import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.data.redis.core.RedisCallback;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisCluster;import redis.clients.jedis.JedisCommands; import javax.annotation.Resource;import java.util.ArrayList;import java.util.List;import java.util.UUID; /** * @author superchen * @since 2018/5/16 **/@Componentpublic class RedisDistributedLock{     @Resource    private RedisTemplate<String, Object> redisTemplate;     public static final String UNLOCK_LUA;     static {        StringBuilder sb = new StringBuilder();        sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");        sb.append("then ");        sb.append("    return redis.call(\"del\",KEYS[1]) ");        sb.append("else ");        sb.append("    return 0 ");        sb.append("end ");        UNLOCK_LUA = sb.toString();    }     private final Logger logger = LoggerFactory.getLogger(RedisDistributedLock.class);     public boolean setLock(String key, long expire) {        try {            RedisCallback<String> callback = (connection) -> {                JedisCommands commands = (JedisCommands) connection.getNativeConnection();                String uuid = UUID.randomUUID().toString();                return commands.set(key, uuid, "NX", "PX", expire);            };            String result = redisTemplate.execute(callback);             return !StringUtils.isEmpty(result);        } catch (Exception e) {            logger.error("set redis occured an exception", e);        }        return false;    }     public String get(String key) {        try {            RedisCallback<String> callback = (connection) -> {                JedisCommands commands = (JedisCommands) connection.getNativeConnection();                return commands.get(key);            };            String result = redisTemplate.execute(callback);            return result;        } catch (Exception e) {            logger.error("get redis occured an exception", e);        }        return "";    }     public boolean releaseLock(String key,String requestId) {        // 释放锁的时候,有可能因为持锁之后方法执行时间大于锁的有效期,此时有可能已经被另外一个线程持有锁,所以不能直接删除        try {            List<String> keys = new ArrayList<>();            keys.add(key);            List<String> args = new ArrayList<>();            args.add(requestId);             // 使用lua脚本删除redis中匹配value的key,可以避免由于方法执行时间过长而redis锁自动过期失效的时候误删其他线程的锁            // spring自带的执行脚本方法中,集群模式直接抛出不支持执行脚本的异常,所以只能拿到原redis的connection来执行脚本            RedisCallback<Long> callback = (connection) -> {                Object nativeConnection = connection.getNativeConnection();                // 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行                // 集群模式                if (nativeConnection instanceof JedisCluster) {                    return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args);                }                 // 单机模式                else if (nativeConnection instanceof Jedis) {                    return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args);                }                return 0L;            };            Long result = redisTemplate.execute(callback);             return result != null && result > 0;        } catch (Exception e) {            logger.error("release lock occured an exception", e);        } finally {            // 清除掉ThreadLocal中的数据,避免内存溢出            //lockFlag.remove();        }        return false;    } }

首先我们要通过Maven引入Jedis开源组件,在pom.xml文件加入下面的代码:

<dependency>    <groupId>redis.clients</groupId>    <artifactId>jedis</artifactId>    <version>2.9.0</version></dependency>

不管是加锁还是解锁,都要保持操作的原子性,否则就有可能产生死锁。上面的setNX方法不再赘述,不懂的童鞋可以自行百度。

另再附上redis初始化的配置及方法,项目使用的是spring cloud

配置文件中的配置:

#redis 配置# Redis数据库索引(默认为0)spring.redis.database=3# Redis服务器地址spring.redis.host=10.10.10.10# Redis服务器连接端口spring.redis.port=6379# Redis服务器连接密码(默认为空)spring.redis.password=1234# 连接池最大连接数(使用负值表示没有限制)spring.redis.pool.max-active=5# 连接池最大阻塞等待时间(使用负值表示没有限制)spring.redis.pool.max-wait=1000# 连接池中的最大空闲连接spring.redis.pool.max-idle=2# 连接池中的最小空闲连接spring.redis.pool.min-idle=0# 连接超时时间(毫秒)spring.redis.timeout=1000

初始化链接的方法:

import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.cache.CacheManager;import org.springframework.cache.annotation.CachingConfigurerSupport;import org.springframework.cache.annotation.EnableCaching;import org.springframework.cache.interceptor.KeyGenerator;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.cache.RedisCacheManager;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import java.lang.reflect.Method;  @Configuration@EnableCachingpublic class RedisConfig extends CachingConfigurerSupport {     @Bean    public KeyGenerator keyGenerator() {        return new KeyGenerator() {             @Override            public Object generate(Object target, Method method, Object... params) {                StringBuilder sb = new StringBuilder();                sb.append(target.getClass().getName());                sb.append(method.getName());                for (Object obj : params) {                    sb.append(obj.toString());                }                return sb.toString();            }        };     }     /**     * 管理缓存     *     * @param redisTemplate     * @return     */     @SuppressWarnings("rawtypes")    @Bean    public CacheManager CacheManager(RedisTemplate redisTemplate) {        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);        return rcm;    }      /**     * redisTemplate配置     *     * @param factory     * @return     */    @SuppressWarnings({ "rawtypes", "unchecked" })    @Bean    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {        StringRedisTemplate template = new StringRedisTemplate(factory);        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);        ObjectMapper om = new ObjectMapper();        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);        jackson2JsonRedisSerializer.setObjectMapper(om);        template.setValueSerializer(jackson2JsonRedisSerializer);        template.afterPropertiesSet();        return template;    } }

点这里可以跳转到人工智能网站

发表评论