redis分布式锁终极代码

博主还有个用于大学生学习英语的项目。其中有个场景是老师和学生不能并发操作的。此时就要使用互斥锁去解决这个问题。因为我们的项目是一个nginx反代5个springboot jar包,所以不能使用synchronized或者Lock。博主选用了redis分布式锁来解决这个问题。

但是网上关于redis分布式锁的设计多种多样,纷繁复杂,令人眼花缭乱。最后在参考网站各个大神的资料还有自己压测后,得出了下面的代码。部署上线后,未发现有问题。

下面是代码请大家参考指正。

  1. redis锁工具代码 :
package com.yx.tsenglish.utils;

import com.yx.tsenglish.module.user.controller.UserController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.xml.crypto.Data;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * desc: redis分布式锁
 * date: 2019/6/11 0011
 *
 * @author cuihao
 */
public class RedisLockTool {
    private final static Logger log = LoggerFactory.getLogger(RedisLockTool.class);

    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";

    /**
     * desc: 获取当前时间日时分秒毫秒
     * date: 2019/9/26
     * @author hewenhua
     * @return
     */
    public static Double getNowTime(){
        Calendar nowtime = new GregorianCalendar();
        StringBuilder time = new StringBuilder();
        time.append(String.format("%02d", nowtime.get(Calendar.HOUR)));
        time.append(String.format("%02d", nowtime.get(Calendar.MINUTE)));
        time.append(String.format("%02d", nowtime.get(Calendar.SECOND)));
        time.append(String.format("%03d", nowtime.get(Calendar.MILLISECOND)));
        return Double.valueOf(time.toString());
    }

    /**
     * 尝试获取分布式锁
     * @param jedisPool jedisPool
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(JedisPool jedisPool, String lockKey, String requestId, int expireTime) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();

            String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }

        } catch (Exception e) {
            log.error("尝试获取分布式锁error:",e);
        }finally {
            jedis.close();
        }
        return false;
    }


    private static final Long RELEASE_SUCCESS = 1L;

    /**
     * 释放分布式锁
     * @param jedisPool jedisPool
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(JedisPool jedisPool, String lockKey, String requestId) {
        int num = 0;
        boolean flag = true;

        while (flag){
            if(num > 2){
                return false;
            }
            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;
                }
            } catch (Exception e) {
                log.error("释放分布式锁error:",e);
            }finally {
                jedis.close();
                num ++;
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    log.error("sleep50ms error:",e);
                }
            }
        }
        return false;
    }
}

  1. jedisPool代码:
    @Bean
    public JedisPool redisPoolFactory()  throws Exception {
        log.info("redis地址:" + host + ":" + port);
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 是否启用pool的jmx管理功能, 默认true
        jedisPoolConfig.setJmxEnabled(true);
        //默认超时时间2000ms
        int redisTimeout = 5000;
        //设置最大连接数500
        jedisPoolConfig.setMaxTotal(jedisMaxActive);
        //设置最大空闲连接50
        jedisPoolConfig.setMaxIdle(jedisMaxIdle);
        //设置等待时间30s
        jedisPoolConfig.setMaxWaitMillis(jedisMaxWait.getSeconds() * 1000);
        //在获取Jedis连接时,自动检验连接是否可用
        jedisPoolConfig.setTestOnBorrow(true);
        //在将连接放回池中前,自动检验连接是否有效
        jedisPoolConfig.setTestOnReturn(true);
        //自动测试池中的空闲连接是否都是可用连接
        jedisPoolConfig.setTestWhileIdle(true);
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, redisTimeout, password);
        return jedisPool;
    }
  1. 使用代码
    public JsonResult onlinecount(String startTime, String endTime,@RequestParam(name = "schoolId", defaultValue = "0", required = false) Long schoolId) {
        String lockKey = "onlinecount" + schoolId;
        String requestId = UUID.randomUUID().toString();
        //加锁300秒钟
        int expireTime = 300 * 1000;
        //响应最大值10秒钟
        int responseTime = 10 * 1000;
        Long nowTime = new Date(System.currentTimeMillis()).getTime();
        boolean isRun = true;
        while (isRun) {
            Long nextTime = new Date(System.currentTimeMillis()).getTime();
            long duration = nextTime - nowTime;
            if (duration >= responseTime) {
                //10秒钟没有抢到锁,自动放弃
                log.info("onlinecount duration:" + duration);
                return new JsonResult(false, "正在努力查询中,请稍等!");
            }
            try {
                //尝试获得锁
                boolean getLock = RedisLockTool.tryGetDistributedLock(jedisPool, lockKey, requestId, expireTime);
                if (getLock) {
					
					//下面是自己的业务逻辑
         

                    //释放锁
                    RedisLockTool.releaseDistributedLock(jedisPool, lockKey, requestId);
                    return new JsonResult(true, "查询成功!", userOnlineVo);
                }
            } catch (Exception e) {
                log.error("  " + lockKey + "锁报错 ", e);
                isRun = false;
                //释放锁
                RedisLockTool.releaseDistributedLock(jedisPool, lockKey, requestId);
            } finally {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    log.error("onlinecount sleep:",e);
                }
            }
        }
        return new JsonResult(true, "查询失败!");
    }
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页