分布式事务解决方案之Redis实现

spring boot 版本 2.1.6.RELEASE

 

 

 

下面是分布式锁的获取和释放,成员变量:StringRedisTemplate(springboot整合redis获取redis,也可通过注入jedis实现,实现思路是一致的,我会在文末附上获取redis的实现)。

再来看看一些redis的基本命令: 
SETNX key value (stringRedisTemplate.opsForValue().setIfAbsent())
如果key不存在,就设置key对应字符串value。在这种情况下,该命令和SET一样。当key已经存在时,就不做任何操作。SETNX是”SET if Not eXists”。 
expire KEY seconds (stringRedisTemplate.expire())
设置key的过期时间。如果key已过期,将会被自动删除。 
del KEY (stringRedisTemplate.delete())
删除key 


分布式锁的获取与释放

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author fanxuebo
 * @description 分布式锁
 * @company 
 * @create 2019/1/10 17:48
 */
public class DistributedLock {

    private static final Logger LOGGER = LoggerFactory.getLogger(DistributedLock.class);

    private StringRedisTemplate stringRedisTemplate;

    public DistributedLock(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * @Author fanxuebo
     * @Date 2019/1/11 10:10
     * @Description 获取锁,锁的名称,获取锁超时时间,释放锁的超时时间
     **/
    public String lockWithTimeout(String lockName, long timeout) {
        String retIdentifier = null;
        try {
            String identifier = UUID.randomUUID().toString();// 随机生成一个value
            String lockKey = "lock:" + lockName;// 锁名,即key值
            int lockExpire = (int) (timeout / 1000);// 超时时间,上锁后超过此时间则自动释放锁
            while (true) {
                if (stringRedisTemplate.opsForValue().setIfAbsent(lockKey, identifier)) {
                    LOGGER.info("{}:获取到锁lockKey:{}", Thread.currentThread().getName(), lockKey);
                    stringRedisTemplate.expire(lockKey, lockExpire, TimeUnit.SECONDS);
                    retIdentifier = identifier;// 返回value值,用于释放锁时间确认
                    break;
                }
                // 返回-1代表key没有设置超时时间,为key设置一个超时时间
                if (stringRedisTemplate.getExpire(lockKey) == -1L) {
                    stringRedisTemplate.expire(lockKey, lockExpire, TimeUnit.SECONDS);
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("获取锁异常:[{}]", e.getStackTrace()[0]);
        }
        return retIdentifier;
    }

    /**
     * @Author fanxuebo
     * @Date 2019/1/11 10:09
     * @Description 释放锁
     **/
    public boolean releaseLock(String lockName, String identifier) {
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        try {
            while (true) {
                stringRedisTemplate.watch(lockKey);// 监视lock,准备开始事务
                // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁
                if (identifier.equals(stringRedisTemplate.opsForValue().get(lockKey))) {
                    LOGGER.info("{}:删除锁lockKey:{}", Thread.currentThread().getName(), lockKey);
                    stringRedisTemplate.delete(lockKey);
                    retFlag = true;
                }
                stringRedisTemplate.unwatch();
                break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("释放锁异常:[{}]", e.getStackTrace()[0]);
        }
        return retFlag;
    }
}

在需要上锁的代码块只需做如下操作:

DistributedLock distributedLock = new DistributedLock(stringRedisTemplate);
String lockName = XXX;
String identifier = null;
try {
    identifier = distributedLock.lockWithTimeout(lockName, 3000);
    //需要控制事务的代码
    distributedLock.releaseLock(lockName, identifier);
} catch (Exception e) {
    distributedLock.releaseLock(lockName, identifier);
}

在获取锁方法处可做改进,传入获取锁的时间,在一定时间内获取,若未获取到返回的标识为NULL。

long end = System.currentTimeMillis() + acquireTimeout;
while (System.currentTimeMillis() < end) {}
DistributedLock distributedLock = new DistributedLock(stringRedisTemplate);
String lockName = XXX;
String identifier = null;
try {
    identifier = distributedLock.lockWithTimeout(lockName, 3000);
    if (StringUtils.isBlank(identifier)) {
        //获取锁等待超时的处理
    }
    //需要控制事务的代码
    distributedLock.releaseLock(lockName, identifier);
} catch (Exception e) {
    distributedLock.releaseLock(lockName, identifier);
}

springboot整合reids:

扫描二维码关注公众号,回复: 11354928 查看本文章
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <!-- <version>2.1.6.RELEASE</version> -->
</dependency>
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author fanxuebo
 * @description redis配置类
 * @company 
 * @createDate 2019-11-27 08:46:17 星期三
 */
@Configuration
public class RedisConfiguration {

    /**
     * @Author fanxuebo
     * @Date 2019/11/27 9:05
     * @Description jdk序列方式,用来保存对象
     **/
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        return getRedisTemplate(redisTemplate, redisConnectionFactory);
    }

    /**
     * @Author fanxuebo
     * @Date 2019/11/27 9:06
     * @Description string序列方式,用于存储字符串格式
     **/
    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        return (StringRedisTemplate) getRedisTemplate(stringRedisTemplate, redisConnectionFactory);
    }

    private RedisTemplate getRedisTemplate(RedisTemplate redisTemplate, RedisConnectionFactory redisConnectionFactory) {
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置value的序列化规则和 key的序列化规则
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}


配置文件:

spring:
  redis:
    database: 
    host: 
    port: 6379
    password: 
    timeout: 5000
    jedis:
      pool:
        max-active: 50
        max-wait: 5000

希望阅读后会让你们有所收获,本文待完善。。。

猜你喜欢

转载自blog.csdn.net/fanxb92/article/details/86982928