springboot+redis分布式

单类例子

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;

import java.util.List;
import java.util.UUID;

public class Test {
    private static JedisPool jedisPool;
    static{
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(20);
        jedisPoolConfig.setMaxIdle(10);
        jedisPool = new JedisPool(jedisPoolConfig,"39.106.68.198",6379,10000,"123");
    }

    public static Jedis getJedis() throws Exception {
        if(null != jedisPool){
            return jedisPool.getResource();
        }
        throw  new Exception("Jedispool was not init");
    }

    /**
     * 获取分布式锁
     * @param key
     * @param timeout 超时时限
     * @return
     */
    public String getLock(String key,int timeout){
        //TODO 过期时间从缓存中取 配置化
        try {
            //获得客户端实例
            Jedis jedis = Test.getJedis();
            //生成随机锁号,解锁时用于判断,是否为当前线程持有锁
            String value = UUID.randomUUID().toString();
            //不能无限等待,超出时间放弃自旋
            long end = System.currentTimeMillis() + timeout;
            //判断是否超出时限  并且阻塞
            while(System.currentTimeMillis()<end){
                //设置  setnx 成功后 返回1,否则为0
                if(jedis.setnx(key,value)==1){
                    jedis.expire(key,timeout);
                    return value;
                }
                //如果锁获取成功 reids挂掉,判断锁永久有效,再次设置失效时间
                if(jedis.ttl(key)==-1){
                    jedis.expire(key,timeout);
                }
                //不能无限占用资源 设置休眠,然后再次获取锁
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            //TODO 日志
            e.printStackTrace();
        }

        return null;
    }
    /**
     * 释放锁(判断是否为当前线程持有)
     * @param key
     * @param value
     * @return
     */
    public boolean releaseLock(String key,String value){
        try {
            Jedis jedis = Test.getJedis();
            while (true) {
                //监控一个或多个KEY 一旦KEY 被删除 ,后面事务的代码不会被执行
                jedis.watch(key);
                //判断是否为当前线程持有锁
                if(value.equals(jedis.get(key))) {
                    //开启事务
                    Transaction transaction = jedis.multi();
                    transaction.del(key);
                    List<Object> list = transaction.exec();
                    //没有指令集执行成功 解锁失败
                    //TODO 关注事务执行过程(监视解锁前 人为变动锁可能会出现的问题)
                    if (list == null) {
                        continue;
                    }
                    System.out.println("锁释放成功,key【"+key+"】,value【"+value+"】");
                    return true;
                }
                jedis.unwatch();
                break;
            }
        } catch (Exception e) {
            //TODO 添加日志
            System.out.println("进程名称【"+Thread.currentThread().getName()+"】"+"进程ID【"+Thread.currentThread().getId()+"】解锁失败"+e.getCause());
            e.printStackTrace();
        }
        return false;
    }

    public static void main(String[] args) {
        Test test = new Test();
        String key = "lock:testLock1";
        String lockId = test.getLock(key,10000);
        if(null != lockId){
            System.out.println("进程名称【"+Thread.currentThread().getName()+"】"+"进程ID【"+Thread.currentThread().getId()+"】获得锁成功,"+"锁KEY【"+key+"】"+"锁序号:【"+lockId+"】");
        }else{
            System.out.println("进程名称【"+Thread.currentThread().getName()+"】"+"进程ID【"+Thread.currentThread().getId()+"】获得锁失败");
        }
        test.releaseLock("lock:testLock1",lockId);
        System.out.println("完成");


    }

}


整合springboot分布式工具类

controller层


import com.example.demo.conf.JedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
public class RedisController {


    @Autowired
    private JedisUtil jedisUtil;

    @RequestMapping("wordsList")
    public void redis(String userId) {
        String lockKey = userId;//锁key
        String requestId = UUID.randomUUID().toString();//请求标识
        final int EXPIRED_TIME = 300 * 1000;//redis 数据存储过期时间

        //加锁举例
        boolean lockResult = jedisUtil.tryGetDistributedLock(lockKey, requestId, EXPIRED_TIME);

        System.out.println("加锁成功,"+lockResult);

        //放锁举例
        boolean releaseResult = jedisUtil.releaseDistributedLock(lockKey, requestId);
        System.out.println("放锁成功,"+releaseResult);
    }
}

application.properties 配置

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

加载Redis配置工具类 注册到容器中

package com.example.demo.conf;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    protected static final Logger logger = LoggerFactory.getLogger(RedisConfig.class);

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

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

    @Value("${spring.redis.jedis.pool.max-active}")
    private int maxTotal;

    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;

    @Value("${spring.redis.jedis.pool.min-idle}")
    private int minIdle;

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

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

    @Bean
    public JedisPool jedisPool() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxTotal);
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
        logger.info("JedisPool注入成功!!");
        logger.info("redis地址:" + host + ":" + port);
        return jedisPool;
    }
}

Jedis的工具类(使用JedisPool)

package com.example.demo.conf;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Collections;

/**
 * 不采用springTemplate 的操作类
 * 因为springTemplate 的SetNx 非原子性,可能导致锁永久锁住,释放失败
 */
@Component("jedisUtil")
public class JedisUtil {

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    private static final Long RELEASE_SUCCESS = 1L;

    @Autowired
    private JedisPool jedisPool;

    /**
     * 尝试获取分布式锁
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
        Jedis jedis = null;
        try{
            jedis = jedisPool.getResource();
//            Long result = jedis.setnx(lockKey,requestId);
            String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }
        } finally {
            //归还 jedis 连接
            if(jedis != null){
                jedis.close();
            }
        }
        return false;
    }

    /**
     * 释放分布式锁
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public boolean releaseDistributedLock(String lockKey, String requestId) {
        Jedis jedis = null;
        try {
            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));
            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
        } finally {
            //归还 jedis 连接
            if(jedis != null){
                jedis.close();
            }
        }
        return false;
    }

    /**
     * 设置值并设置超时时间
     * @param key
     * @param value
     * @param expireTime
     * @return
     */
    public Long rpushString(String key, String value, int expireTime){
        Jedis jedis = null;
        Long result = 0L;
        try {
            jedis = jedisPool.getResource();
            result = jedis.rpush(key, value);
            if(result > 0){
                jedis.expire(key,expireTime);
            }
        } finally {
            if(jedis != null){
                jedis.close();
            }
        }
        return result;
    }

    /**
     * 批量插入
     * @param key
     * @param values
     * @param expireTime
     * @return
     */
    public Long batchRpushString(String key, String[] values, int expireTime){
        Jedis jedis = null;
        Long result = 0L;
        try {
            jedis = jedisPool.getResource();
            result = jedis.rpush(key, values);
            if(result > 0){
                jedis.expire(key,expireTime);
            }
        } finally {
            if(jedis != null){
                jedis.close();
            }
        }
        return result;

    }

    /**
     * 释放锁
     * @param lockKey
     */
    public Long releaseLock(String lockKey){
        Long result = 0L;
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            result = jedis.del(lockKey);
        } finally {
            if(jedis != null){
                jedis.close();
            }
        }
        return result;
    }

}


总结

无论是单类版的jedis.setnx 还是整合版的jedis.set();都是通过Jedis类中的 SafeEncoder.encode(key)进行加锁的原理

链接: https://pan.baidu.com/s/1M7OhtwFy6NflAiMkLClKdQ
提取码:fh1h

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值