博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Redis分布式锁
阅读量:5093 次
发布时间:2019-06-13

本文共 5388 字,大约阅读时间需要 17 分钟。

目前几种分布式锁的实现方式:

  • 数据库实现(不适合数据量比较大的互联网公司)、
  • 基于ZK的实现(1、Zk的节点改变时候的watcher事件通知。2、节点类型中的有序节点可实现先到先得公平策略)
  • 基于Redis的实现(setNX + 有效期,实现相对ZK简单一些)    

工作中经常用到Redis,所以决定采用redis实现分布式锁, 首先先要明确目标,目标明确了才有技术方案

分布式锁实现的目标

  •  高性能(加锁和解锁性能高)
  •  互斥访问(一个线程持有锁,另一个线程不能持有)
  •  不能产生死锁(例如redis客户端挂了,或者设置过期时间时候挂了导致key永久有效)
  •   解锁(只能解除自己的锁)

具体实现:

 
1 package com.brightcns.wuxi.citizencard.common.feature.util;  2   3 import javafx.beans.binding.ObjectExpression;  4 import lombok.extern.slf4j.Slf4j;  5 import org.springframework.data.redis.connection.RedisConnection;  6 import org.springframework.data.redis.connection.ReturnType;  7 import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;  8 import redis.clients.jedis.JedisPoolConfig;  9 import redis.clients.util.SafeEncoder; 10  11  12 /** 13  * 分布式锁的常用类 14  * @author maxianming 15  * @date 2018/8/13 15:50 16  */ 17 @Slf4j 18 public class LockUtils { 19  20     private JedisConnectionFactory jedisConnectionFactory; 21     private static final Long RELEASE_SUCCESS = 1L; 22     private static final String LOCK_SUCCESS = "OK"; 23     private static final String SET_IF_NOT_EXIST = "NX"; 24     private static final String SET_WITH_EXPIRE_TIME = "PX"; 25  26     public LockUtils(JedisConnectionFactory jedisConnectionFactory) { 27         if (jedisConnectionFactory == null) { 28           throw new IllegalArgumentException("jedisConnectionFactory not be allowed null"); 29         } 30         this.jedisConnectionFactory = jedisConnectionFactory; 31     } 32     /** 33      * 尝试获取分布式锁 34      * @param lockKey 锁 35      * @param requestId 请求标识 36      * @param expireTime 超期时间 ms 37      * @return 是否获取成功 38      */ 39     public void lock(String lockKey, String requestId, int expireTime) throws InterruptedException { 40         RedisConnection redisConnection = getRedisConnection(); 41         try { 42           while (true) { 43                Object result = redisConnection.execute("SET", new byte[][]{ 44                        SafeEncoder.encode(lockKey), SafeEncoder.encode(requestId), SafeEncoder.encode(SET_IF_NOT_EXIST), 45                        SafeEncoder.encode(SET_WITH_EXPIRE_TIME), SafeEncoder.encode(String.valueOf(expireTime))}); 46                if (result != null) { 47                    if (LOCK_SUCCESS.equals(new String((byte[])(result)))) { 48                        return; 49                    } 50                } 51               try { 52                   Thread.sleep(expireTime); 53               } catch (InterruptedException e) { 54                   log.error("中断异常", e); 55                   throw e; 56               } 57           } 58        } finally { 59            redisConnection.close(); 60        } 61     } 62  63  64     /** 65      * 释放分布式锁 66      * @param lockKey 锁 67      * @param requestId 请求标识 68      * @return 是否释放成功 69      */ 70     public boolean unLock(String lockKey, String requestId) { 71         RedisConnection redisConnection = getRedisConnection(); 72         try { 73             String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; 74             Object result = redisConnection.eval(SafeEncoder.encode(script), ReturnType.INTEGER, 1, getByteParams(new String[]{lockKey, requestId})); 75             if (RELEASE_SUCCESS.equals(result)) { 76                 return true; 77             } 78             return false; 79         } finally { 80             redisConnection.close(); 81         } 82  83     } 84  85     private byte[][] getByteParams(String... params) { 86         byte[][] p = new byte[params.length][]; 87         for (int i = 0; i < params.length; i++) 88             p[i] = SafeEncoder.encode(params[i]); 89  90         return p; 91     } 92  93     private RedisConnection getRedisConnection() { 94         RedisConnection redisConnection = jedisConnectionFactory.getConnection(); 95         return redisConnection; 96     } 97  98     public static void main(String[] args) { 99        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();100         jedisConnectionFactory.setPort(6379);101         jedisConnectionFactory.setHostName("*");102         jedisConnectionFactory.setPassword("*");103 104         JedisPoolConfig poolConfig = new JedisPoolConfig();105         poolConfig.setMaxTotal(40);106         poolConfig.setMaxIdle(10);107         poolConfig.setMinIdle(5);108         jedisConnectionFactory.setPoolConfig(poolConfig);109         jedisConnectionFactory.afterPropertiesSet();110         LockUtils lockUtils =new LockUtils(jedisConnectionFactory);111         Thread thread = new Thread(() -> {112             try {113                 lockUtils.lock("lock", "123", 20000);114             } catch (InterruptedException e) {115                 e.printStackTrace();116             }117             System.out.println("加锁结果:" + "成功");118 119             boolean result = lockUtils.unLock("lock", "123");120 121             System.out.println("解锁结果:" + result);122         },"jedis-thread");123         thread.start();128     }129 130 }
 

 

 

   (1)加锁

           jedis提供了set方法将setNX和expire整合在一起的原子方法,这就避免了expire之前redis挂了导致的key永久有效,从而死锁。

          实际项目中使用的Spring boot整合的redis,所以直接获取jedis客户端不太方便,查看源码发现 存在execute支持redis命令。

          requestId的作用,解锁的时候传入加锁时候的相同值,避免错误解除别的线程的锁

          expireTime的作用,redis key的有效期,根据实际时间设置

 (2)解锁

          采用lua脚本,主要保证一、解锁操作的原子性 二、解的是自己的锁

转载于:https://www.cnblogs.com/mxmbk/p/9470590.html

你可能感兴趣的文章
windows下安装nodejs
查看>>
[LeetCode] Trapping Rain Water II 题解
查看>>
对排名前3000位博主进行数据分析
查看>>
Python设计模式之单例模式
查看>>
eclipse中server location为灰色,不能修改
查看>>
shift and算法
查看>>
MongoDB数据库导出导入迁移
查看>>
CentOS 7 重装mysql编译过程报错解决方法
查看>>
《你必须知道的.NET》书中对OCP(开放封闭)原则的阐述
查看>>
最短路+状压DP【洛谷P3489】 [POI2009]WIE-Hexer
查看>>
LightOJ - 1050 (唯一分解+推公式+乘法逆元)
查看>>
线性模型的概率分析
查看>>
unity中动态生成网格
查看>>
windows下docker的安装及常用命令学习
查看>>
一个自己主动依据xcode中的objective-c代码生成类关系图的神器
查看>>
leetCode 103.Binary Tree Zigzag Level Order Traversal (二叉树Z字形水平序) 解题思路和方法...
查看>>
等额本金-c语言俩个整数除法
查看>>
观察者模式
查看>>
(二十一)访问者模式-代码实现
查看>>
判断 wp 是否是活跃页面
查看>>