设为首页 收藏本站
查看: 2249|回复: 0

[经验分享] 基于Redis的分布式锁实现

[复制链接]

尚未签到

发表于 2017-12-21 23:29:14 | 显示全部楼层 |阅读模式
  工作中涉及到了不同服务器并发获取Token的需求,但是后一次获取会覆盖前一次获取的Token,因此需要对获取Token这一操作做一次分布式加锁。这次我使用redis来解决这个问题,首先提供一个加锁的类:
import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.dao.DataAccessException;import org.springframework.data.redis.connection.RedisConnection;import org.springframework.data.redis.core.RedisCallback;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.serializer.RedisSerializer;import java.util.concurrent.TimeUnit;/** * 基于redis的分布式锁实现 */public>    private static final Logger LOG = LoggerFactory.getLogger(RedisLockImpl.class);    //加锁超时时间,单位毫秒, 即:加锁时间内执行完操作,如果未完成会有并发现象    private long lockTimeout;    private StringRedisTemplate redisTemplate;    public RedisLockImpl(StringRedisTemplate redisTemplate, long timeout) {        this.redisTemplate = redisTemplate;        this.lockTimeout = timeout;    }    /**     * 加锁     * 取到锁加锁,取不到锁就返回     *     * @param lockKey     * @param threadName     * @return     */    @Override    public synchronized long lock(String lockKey, String threadName) {        LOG.debug(threadName + &quot;开始执行加锁&quot;);        //锁时间        Long lock_timeout = currtTimeForRedis() + lockTimeout + 1;        if (redisTemplate.execute(new RedisCallback<Boolean>() {            @Override            public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {                //定义序列化方式                RedisSerializer<String> serializer = redisTemplate.getStringSerializer();                byte[] value = serializer.serialize(lock_timeout.toString());                boolean flag = redisConnection.setNX(lockKey.getBytes(), value);                return flag;            }        })) {            //如果加锁成功            LOG.debug(threadName + &quot;加锁成功+1&quot;);            //设置超时时间,释放内存            redisTemplate.expire(lockKey, lockTimeout, TimeUnit.MILLISECONDS);            return lock_timeout;        } else {            //获取redis里面的时间            String result = redisTemplate.opsForValue().get(lockKey);            Long currt_lock_timeout_str = result == null ? null : Long.parseLong(result);            //锁已经失效            if (currt_lock_timeout_str != null && currt_lock_timeout_str < System.currentTimeMillis()) {                //判断是否为空,不为空时,说明已经失效,如果被其他线程设置了值,则第二个条件判断无法执行                //获取上一个锁到期时间,并设置现在的锁到期时间                Long old_lock_timeout_Str = Long.valueOf(redisTemplate.opsForValue().getAndSet(lockKey, lock_timeout.toString()));                if (old_lock_timeout_Str != null && old_lock_timeout_Str.equals(currt_lock_timeout_str)) {                    //多线程运行时,多个线程签好都到了这里,但只有一个线程的设置值和当前值相同,它才有权利获取锁                    LOG.debug(threadName + &quot;加锁成功+2&quot;);                    //设置超时间,释放内存                    redisTemplate.expire(lockKey, lockTimeout, TimeUnit.MILLISECONDS);                    //返回加锁时间                    return lock_timeout;                }            }        }        return -1;    }    /**     * 解锁     *     * @param lockKey     * @param lockValue     * @param threadName     */    @Override    public synchronized void unlock(String lockKey, long lockValue, String threadName) {        LOG.debug(threadName + &quot;执行解锁==========&quot;);//正常直接删除 如果异常关闭判断加锁会判断过期时间        //获取redis中设置的时间        String result = redisTemplate.opsForValue().get(lockKey);        Long currt_lock_timeout_str = result == null ? null : Long.valueOf(result);        //如果是加锁者,则删除锁, 如果不是,则等待自动过期,重新竞争加锁        if (currt_lock_timeout_str != null && currt_lock_timeout_str == lockValue) {            redisTemplate.delete(lockKey);            LOG.debug(threadName + &quot;解锁成功------------------&quot;);        }    }    /**     * 多服务器集群,使用下面的方法,代替System.currentTimeMillis(),获取redis时间,避免多服务的时间不一致问题!!!     *     * @return     */    @Override    public long currtTimeForRedis() {        return redisTemplate.execute(new RedisCallback<Long>() {            @Override            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {                return redisConnection.time();            }        });    }}  使用方法:
  

  

            if ((lockTime = redisLock.lock(tokenLockName, threadName)) != null) {                //开始执行任务                int tryCount = 3;                while (token == null && --tryCount > 0) {                    token = getTokenInternal(username, password);                }                //加入redis缓存                if (token != null)                    redisService.set(String.format(ACCESS_TOKEN_KEY_TEMPLATE, username), token, tokenTimeout);                else                    logger.info(&quot;{}获取token失败!&quot;, username);                //任务执行完毕 关闭锁                redisLock.unlock(tokenLockName, lockTime, threadName);            }

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-426667-1-1.html 上篇帖子: 【redis专题(10)】KEY设计原则与技巧 下篇帖子: redis和memcached的区别(总结)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表