基于Consul的分布式锁实现

开发 开发工具 分布式
本文将介绍一种基于Consul 的Key/Value存储来实现分布式锁以及信号量的方法。

[[188445]]

我们在构建分布式系统的时候,经常需要控制对共享资源的互斥访问。这个时候我们就涉及到分布式锁(也称为全局锁)的实现,基于目前的各种工具,我们已经有了大量的实现方式,比如:基于Redis的实现、基于Zookeeper的实现。本文将介绍一种基于Consul 的Key/Value存储来实现分布式锁以及信号量的方法。

分布式锁实现

基于Consul的分布式锁主要利用Key/Value存储API中的acquire和release操作来实现。acquire和release操作是类似Check-And-Set的操作:

  • acquire操作只有当锁不存在持有者时才会返回true,并且set设置的Value值,同时执行操作的session会持有对该Key的锁,否则就返回false
  • release操作则是使用指定的session来释放某个Key的锁,如果指定的session无效,那么会返回false,否则就会set设置Value值,并返回true

具体实现中主要使用了这几个Key/Value的API:

create session:https://www.consul.io/api/session.html#session_create

delete session:https://www.consul.io/api/session.html#delete-session

KV acquire/release:https://www.consul.io/api/kv.html#create-update-key

基本流程

 

 

具体实现

  1. public class Lock { 
  2.   
  3.     private static final String prefix = "lock/";  // 同步锁参数前缀 
  4.   
  5.     private ConsulClient consulClient; 
  6.     private String sessionName; 
  7.     private String sessionId = null
  8.     private String lockKey; 
  9.   
  10.     /** 
  11.      * 
  12.      * @param consulClient 
  13.      * @param sessionName   同步锁的session名称 
  14.      * @param lockKey       同步锁在consul的KV存储中的Key路径,会自动增加prefix前缀,方便归类查询 
  15.      */ 
  16.     public Lock(ConsulClient consulClient, String sessionName, String lockKey) { 
  17.         this.consulClient = consulClient; 
  18.         this.sessionName = sessionName; 
  19.         this.lockKey = prefix + lockKey; 
  20.     } 
  21.   
  22.     /** 
  23.      * 获取同步锁 
  24.      * 
  25.      * @param block     是否阻塞,直到获取到锁为止 
  26.      * @return 
  27.      */ 
  28.     public Boolean lock(boolean block) { 
  29.         if (sessionId != null) { 
  30.             throw new RuntimeException(sessionId + " - Already locked!"); 
  31.         } 
  32.         sessionId = createSession(sessionName); 
  33.         while(true) { 
  34.             PutParams putParams = new PutParams(); 
  35.             putParams.setAcquireSession(sessionId); 
  36.             if(consulClient.setKVValue(lockKey, "lock:" + LocalDateTime.now(), putParams).getValue()) { 
  37.                 return true
  38.             } else if(block) { 
  39.                 continue
  40.             } else { 
  41.                 return false
  42.             } 
  43.         } 
  44.     } 
  45.   
  46.     /** 
  47.      * 释放同步锁 
  48.      * 
  49.      * @return 
  50.      */ 
  51.     public Boolean unlock() { 
  52.         PutParams putParams = new PutParams(); 
  53.         putParams.setReleaseSession(sessionId); 
  54.         boolean result = consulClient.setKVValue(lockKey, "unlock:" + LocalDateTime.now(), putParams).getValue(); 
  55.         consulClient.sessionDestroy(sessionId, null); 
  56.         return result; 
  57.     } 
  58.   
  59.     /** 
  60.      * 创建session 
  61.      * @param sessionName 
  62.      * @return 
  63.      */ 
  64.     private String createSession(String sessionName) { 
  65.         NewSession newSession = new NewSession(); 
  66.         newSession.setName(sessionName); 
  67.         return consulClient.sessionCreate(newSession, null).getValue(); 
  68.     } 
  69.   

单元测试

  1. public class TestLock { 
  2.   
  3.     private Logger logger = Logger.getLogger(getClass()); 
  4.   
  5.     @Test 
  6.     public void testLock() throws Exception  { 
  7.         new Thread(new LockRunner(1)).start(); 
  8.         new Thread(new LockRunner(2)).start(); 
  9.         new Thread(new LockRunner(3)).start(); 
  10.         new Thread(new LockRunner(4)).start(); 
  11.         new Thread(new LockRunner(5)).start(); 
  12.         Thread.sleep(200000L); 
  13.     } 
  14.    
  15.     class LockRunner implements Runnable { 
  16.   
  17.         private Logger logger = Logger.getLogger(getClass()); 
  18.         private int flag; 
  19.   
  20.         public LockRunner(int flag) { 
  21.             this.flag = flag; 
  22.         } 
  23.   
  24.         @Override 
  25.         public void run() { 
  26.             Lock lock = new Lock(new ConsulClient(), "lock-session""lock-key"); 
  27.             try { 
  28.                 if (lock.lock(true)) { 
  29.                     logger.info("Thread " + flag + " start!"); 
  30.                     Thread.sleep(new Random().nextInt(3000L)); 
  31.                     logger.info("Thread " + flag + " end!"); 
  32.                 } 
  33.             } catch (Exception e) { 
  34.                 e.printStackTrace(); 
  35.             } finally { 
  36.                 lock.unlock(); 
  37.             } 
  38.         } 
  39.     } 
  40.    

优化建议

本文我们实现了基于Consul的简单分布式锁,但是在实际运行时,可能会因为各种各样的意外情况导致unlock操作没有得到正确地执行,从而使得分布式锁无法释放。所以为了更完善的使用分布式锁,我们还必须实现对锁的超时清理等控制,保证即使出现了未正常解锁的情况下也能自动修复,以提升系统的健壮性。那么如何实现呢?请持续关注我的后续分解!

【本文为51CTO专栏作者“翟永超”的原创稿件,转载请通过51CTO联系作者获取授权】

戳这里,看该作者更多好文

责任编辑:武晓燕 来源: 51CTO专栏
相关推荐

2017-05-11 14:05:25

Consul分布式信号量

2022-10-27 10:44:14

分布式Zookeeper

2017-10-24 11:28:23

Zookeeper分布式锁架构

2022-11-06 19:28:02

分布式锁etcd云原生

2019-06-19 15:40:06

分布式锁RedisJava

2021-02-28 07:49:28

Zookeeper分布式

2019-02-26 09:51:52

分布式锁RedisZookeeper

2018-04-03 16:24:34

分布式方式

2017-01-16 14:13:37

分布式数据库

2022-04-08 08:27:08

分布式锁系统

2023-08-21 19:10:34

Redis分布式

2022-01-06 10:58:07

Redis数据分布式锁

2021-10-25 10:21:59

ZK分布式锁ZooKeeper

2021-11-01 12:25:56

Redis分布式

2023-12-01 10:49:07

Redis分布式锁

2021-06-03 00:02:43

RedisRedlock算法

2022-03-08 07:22:48

Redis脚本分布式锁

2021-07-30 00:09:21

Redlock算法Redis

2023-03-01 08:07:51

2018-04-09 09:15:32

数据库DB分布式锁
点赞
收藏

51CTO技术栈公众号