61°

利用consul在spring boot中实现最简单的分布式锁

因为在项目实际过程中所采用的是微服务架构,考虑到承载量基本每个相同业务的服务都是多节点部署,所以针对某些资源的访问就不得不用到用到分布式锁了。

这里列举一个最简单的场景,假如有一个智能售货机,由于机器本身的原因不能同一台机器不能同时出两个商品,这就要求在在出货流程前针对同一台机器在同一时刻出现并发

创建订单时只能有一笔订单创建成功,但是订单服务是多节点部署的,所以就不得不用到分布式锁了。

以上只是一种简单的业务场景,在各种大型互联网实际应用中,需要分布式锁的业务场景会更多,综合比较了业界基于各种中间件来实现的分布式锁方案,然后结合实际业务最终

决定采用consul来实现,因为我们的项目中采用了consul做注册中心,并且consul天生可以保证一致性(这点类似zk),当然zk也能实现分布式锁,但是这里不对这点做过多讨论。

redis虽然也能实现分布式锁,但是可能因为场景比较复杂,如果redis采用cluster部署的话,如果某一主节点出现故障的话,有一定几率会出现脑裂现象,这样就可能会让竞争者在

并发时同时获得到锁,这样可能会破坏掉后面的业务,当然出现这种情况的概率很低,但是也不能完全排除,因为redis的根本不能保证强一致性导致的。

 

好了,这里说的最简单的分布式锁的意思是,多个竞争者同一时间并发去获得锁时,获取失败的就直接返回了,获取成功的继续后续的流程,然后在合适的时间释放锁,并且为锁

加了超时时间,防止获得到锁的进程或线程在未来得及释放锁时自己挂掉了,导致资源处于一直被锁定的状态无法得到释放。主要的实现逻辑就是这样,如果有人想实现获得锁失

败的竞争者一直继续尝试获得,可以基于该示例进行修改,加上自旋逻辑就OK。

以下是锁实现代码:

  1 package com.lyb.consullock;
  2 
  3 import com.ecwid.consul.v1.ConsulClient;
  4 import com.ecwid.consul.v1.agent.model.NewCheck;
  5 import com.ecwid.consul.v1.kv.model.PutParams;
  6 import com.ecwid.consul.v1.session.model.NewSession;
  7 import com.ecwid.consul.v1.session.model.Session;
  8 import lombok.Data;
  9 
 10 
 11 import java.time.LocalDateTime;
 12 import java.util.ArrayList;
 13 import java.util.List;
 14 
 15 
 16 public class DistributedLock{
 17     private ConsulClient consulClient;
 18 
 19     /**
 20      * 构造函数
 21      * @param consulHost 注册consul的client或服务端的Ip或主机名,或域名
 22      * @param consulPort 端口号
 23      */
 24     public DistributedLock(String consulHost,int consulPort){
 25         consulClient = new ConsulClient(consulHost,consulPort);
 26     }
 27 
 28     /**
 29      * 获得锁的方法
 30      * @param lockName 竞争的资源名
 31      * @param ttlSeconds 锁的超时时间,超过该时间自动释放
 32      * @return
 33      */
 34     public LockContext getLock(String lockName,int ttlSeconds){
 35         LockContext lockContext = new LockContext();
 36         if(ttlSeconds<10 || ttlSeconds > 86400) ttlSeconds = 60;
 37         String sessionId = createSession(lockName,ttlSeconds);
 38         boolean success = lock(lockName,sessionId);
 39         if(success == false){
 40             consulClient.sessionDestroy(sessionId,null);
 41             lockContext.setGetLock(false);
 42 
 43             return lockContext;
 44         }
 45 
 46         lockContext.setSession(sessionId);
 47         lockContext.setGetLock(true);
 48 
 49         return lockContext;
 50     }
 51 
 52     /**
 53      * 释放锁
 54      * @param sessionID
 55      */
 56     public void releaseLock(String sessionID){
 57         consulClient.sessionDestroy(sessionID,null);
 58     }
 59 
 60     private String createSession(String lockName,int ttlSeconds){
 61         NewCheck check = new NewCheck();
 62         check.setId("check "+lockName);
 63         check.setName(check.getId());
 64         check.setTtl(ttlSeconds+"s"); //该值和session ttl共同决定决定锁定时长
 65         check.setTimeout("10s");
 66         consulClient.agentCheckRegister(check);
 67         consulClient.agentCheckPass(check.getId());
 68 
 69         NewSession session = new NewSession();
 70         session.setBehavior(Session.Behavior.RELEASE);
 71         session.setName("session "+lockName);
 72         session.setLockDelay(1);
 73         session.setTtl(ttlSeconds + "s"); //和check ttl共同决定锁时长
 74         List<String> checks = new ArrayList<>();
 75         checks.add(check.getId());
 76         session.setChecks(checks);
 77         String sessionId = consulClient.sessionCreate(session,null).getValue();
 78 
 79         return sessionId;
 80     }
 81 
 82     private boolean lock(String lockName,String sessionId){
 83         PutParams putParams = new PutParams();
 84         putParams.setAcquireSession(sessionId);
 85 
 86         boolean isSuccess = consulClient.setKVValue(lockName,"lock:"+ LocalDateTime.now(),putParams).getValue();
 87 
 88         return isSuccess;
 89     }
 90 
 91     /**
 92      * 竞争锁时返回的对象
 93      */
 94     @Data
 95     public class LockContext{
 96         /**
 97          * 获得锁成功返回该值,比便后面用该值来释放锁
 98          */
 99         private String session;
100         /**
101          * 是否获得到锁
102          */
103         private boolean isGetLock;
104     }
105 }

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lyb</groupId>
    <artifactId>consul-lock</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>consul-lock</name>
    <description>Demo project for Spring Boot</description>
<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">properties</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">java.version</span><span style="color: #0000ff;">&gt;</span>1.8<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">java.version</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">spring-cloud.version</span><span style="color: #0000ff;">&gt;</span>Greenwich.SR2<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">spring-cloud.version</span><span style="color: #0000ff;">&gt;</span>
<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">properties</span><span style="color: #0000ff;">&gt;</span>

<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependencies</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>org.springframework.boot<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>spring-boot-starter-web<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>org.springframework.cloud<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>spring-cloud-starter-consul-discovery<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>org.springframework.boot<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>spring-boot-starter-actuator<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>org.projectlombok<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>lombok<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">version</span><span style="color: #0000ff;">&gt;</span>1.18.8<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">version</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">optional</span><span style="color: #0000ff;">&gt;</span>true<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">optional</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>

    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>org.springframework.boot<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>spring-boot-starter-test<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">scope</span><span style="color: #0000ff;">&gt;</span>test<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">scope</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependencies</span><span style="color: #0000ff;">&gt;</span>

<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependencyManagement</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependencies</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
            <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>org.springframework.cloud<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
            <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>spring-cloud-dependencies<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
            <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">version</span><span style="color: #0000ff;">&gt;</span>${spring-cloud.version}<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">version</span><span style="color: #0000ff;">&gt;</span>
            <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">type</span><span style="color: #0000ff;">&gt;</span>pom<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">type</span><span style="color: #0000ff;">&gt;</span>
            <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">scope</span><span style="color: #0000ff;">&gt;</span>import<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">scope</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependency</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependencies</span><span style="color: #0000ff;">&gt;</span>
<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">dependencyManagement</span><span style="color: #0000ff;">&gt;</span>

<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">build</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">plugins</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">plugin</span><span style="color: #0000ff;">&gt;</span>
            <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>org.springframework.boot<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">groupId</span><span style="color: #0000ff;">&gt;</span>
            <span style="color: #0000ff;">&lt;</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>spring-boot-maven-plugin<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">artifactId</span><span style="color: #0000ff;">&gt;</span>
        <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">plugin</span><span style="color: #0000ff;">&gt;</span>
    <span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">plugins</span><span style="color: #0000ff;">&gt;</span>
<span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">build</span><span style="color: #0000ff;">&gt;</span>

</project>

测试代码:

package com.lyb.consullock;

import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner;

import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit;

@RunWith(SpringRunner.class) @SpringBootTest public class ConsulLockApplicationTests { @Autowired private ServiceConfig serviceConfig; @Test public void lockSameResourer() { //针对相同资源在同一时刻只有一个线程会获得锁 ExecutorService threadPool = Executors.newFixedThreadPool(10); for (int a=0;a<20;a++){ threadPool.submit( () -> { for (int i = 0;i < 100; i++) { DistributedLock lock = new DistributedLock( serviceConfig.getConsulRegisterHost(), serviceConfig.getConsulRegisterPort());

                        DistributedLock.LockContext lockContext </span>= lock.getLock("test lock", 10<span style="color: #000000;">);
                        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (lockContext.isGetLock()) {
                            System.out.println(Thread.currentThread().getName() </span>+ "获得了锁"<span style="color: #000000;">);
                            </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
                                TimeUnit.SECONDS.sleep(</span>1<span style="color: #000000;">);
                                lock.releaseLock(lockContext.getSession());
                            } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }</span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
                            </span><span style="color: #008000;">//</span><span style="color: #008000;">System.out.println(Thread.currentThread().getName() + "没有获得锁");</span>

} } }); }

    </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
        TimeUnit.MINUTES.sleep(</span>2<span style="color: #000000;">);
    } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
        e.printStackTrace();
    }
}

@Test
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> lockDiffResource(){
    </span><span style="color: #008000;">//</span><span style="color: #008000;">针对不通的资源所有线程都应该能获得锁</span>
    ExecutorService threadPool = Executors.newFixedThreadPool(10<span style="color: #000000;">);
    </span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> a=0;a&lt;20;a++<span style="color: #000000;">){
        threadPool.submit(
                () </span>-&gt;<span style="color: #000000;"> {
                    </span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = 0;i &lt; 100; i++<span style="color: #000000;">) {
                        DistributedLock lock </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> DistributedLock(
                                serviceConfig.getConsulRegisterHost(),
                                serviceConfig.getConsulRegisterPort());

                        DistributedLock.LockContext lockContext </span>= lock.getLock("test lock"+Thread.currentThread().getName(), 10<span style="color: #000000;">);
                        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (lockContext.isGetLock()) {
                            System.out.println(Thread.currentThread().getName() </span>+ "获得了锁"<span style="color: #000000;">);
                            </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
                                TimeUnit.SECONDS.sleep(</span>1<span style="color: #000000;">);
                                lock.releaseLock(lockContext.getSession());
                            } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }</span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {
                            </span><span style="color: #008000;">//</span><span style="color: #008000;">System.out.println(Thread.currentThread().getName() + "没有获得锁");</span>

Assert.assertTrue(lockContext.isGetLock()); } } }); }

    </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
        TimeUnit.MINUTES.sleep(</span>2<span style="color: #000000;">);
    } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
        e.printStackTrace();
    }
}

}

 希望对大家有所帮助

项目路径:

https://github.com/wenwuxianren/consul-lock

原文链接:https://www.cnblogs.com/wenwuxianren/p/11181786.html

全部评论: 0

    我有话说: