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

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

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

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

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

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

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

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

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

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

以下是锁实现代码:

 package com.lyb.consullock;

 import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.agent.model.NewCheck;
import com.ecwid.consul.v1.kv.model.PutParams;
import com.ecwid.consul.v1.session.model.NewSession;
import com.ecwid.consul.v1.session.model.Session;
import lombok.Data; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List; public class DistributedLock{
private ConsulClient consulClient; /**
* 构造函数
* @param consulHost 注册consul的client或服务端的Ip或主机名,或域名
* @param consulPort 端口号
*/
public DistributedLock(String consulHost,int consulPort){
consulClient = new ConsulClient(consulHost,consulPort);
} /**
* 获得锁的方法
* @param lockName 竞争的资源名
* @param ttlSeconds 锁的超时时间,超过该时间自动释放
* @return
*/
public LockContext getLock(String lockName,int ttlSeconds){
LockContext lockContext = new LockContext();
if(ttlSeconds<10 || ttlSeconds > 86400) ttlSeconds = 60;
String sessionId = createSession(lockName,ttlSeconds);
boolean success = lock(lockName,sessionId);
if(success == false){
consulClient.sessionDestroy(sessionId,null);
lockContext.setGetLock(false); return lockContext;
} lockContext.setSession(sessionId);
lockContext.setGetLock(true); return lockContext;
} /**
* 释放锁
* @param sessionID
*/
public void releaseLock(String sessionID){
consulClient.sessionDestroy(sessionID,null);
} private String createSession(String lockName,int ttlSeconds){
NewCheck check = new NewCheck();
check.setId("check "+lockName);
check.setName(check.getId());
check.setTtl(ttlSeconds+"s"); //该值和session ttl共同决定决定锁定时长
check.setTimeout("10s");
consulClient.agentCheckRegister(check);
consulClient.agentCheckPass(check.getId()); NewSession session = new NewSession();
session.setBehavior(Session.Behavior.RELEASE);
session.setName("session "+lockName);
session.setLockDelay(1);
session.setTtl(ttlSeconds + "s"); //和check ttl共同决定锁时长
List<String> checks = new ArrayList<>();
checks.add(check.getId());
session.setChecks(checks);
String sessionId = consulClient.sessionCreate(session,null).getValue(); return sessionId;
} private boolean lock(String lockName,String sessionId){
PutParams putParams = new PutParams();
putParams.setAcquireSession(sessionId); boolean isSuccess = consulClient.setKVValue(lockName,"lock:"+ LocalDateTime.now(),putParams).getValue(); return isSuccess;
} /**
* 竞争锁时返回的对象
*/
@Data
public class LockContext{
/**
* 获得锁成功返回该值,比便后面用该值来释放锁
*/
private String session;
/**
* 是否获得到锁
*/
private boolean isGetLock;
}
}

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> <properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<optional>true</optional>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </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 = lock.getLock("test lock", 10);
if (lockContext.isGetLock()) {
System.out.println(Thread.currentThread().getName() + "获得了锁");
try {
TimeUnit.SECONDS.sleep(1);
lock.releaseLock(lockContext.getSession());
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
//System.out.println(Thread.currentThread().getName() + "没有获得锁");
}
}
});
} try {
TimeUnit.MINUTES.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
} @Test
public void lockDiffResource(){
//针对不通的资源所有线程都应该能获得锁
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 = lock.getLock("test lock"+Thread.currentThread().getName(), 10);
if (lockContext.isGetLock()) {
System.out.println(Thread.currentThread().getName() + "获得了锁");
try {
TimeUnit.SECONDS.sleep(1);
lock.releaseLock(lockContext.getSession());
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
//System.out.println(Thread.currentThread().getName() + "没有获得锁");
Assert.assertTrue(lockContext.isGetLock());
}
}
});
} try {
TimeUnit.MINUTES.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

希望对大家有所帮助

项目路径:

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

利用consul在spring boot中实现最简单的分布式锁的更多相关文章

  1. Spring Boot中利用递归算法查询到所有下级用户,并手动进行分页

    Spring Boot中利用递归算法查询到所有下级用户,并手动进行分页 前提:语言用的是kotlin(和Java一样,但更简洁),写下这篇文章用来记录编程过程中遇到的一些难点 1.功能需求 前端用户A ...

  2. spring boot中利用mybatis-generator插件生成代码

    使用Idea在spring boot中集成mybatis-generator,自动生成mapper.xml  model  dao 文件 一.配置 pom.xml 在pom.xml的<plugi ...

  3. 利用 Spring Boot 中的 @ConfigurationProperties,优雅绑定配置参数

    使用 @Value("${property}") 注释注入配置属性有时会很麻烦,尤其是当你使用多个属性或你的数据是分层的时候. Spring Boot 引入了一个可替换的方案 -- ...

  4. spring boot(三):Spring Boot中Redis的使用

    spring boot对常用的数据库支持外,对nosql 数据库也进行了封装自动化. redis介绍 Redis是目前业界使用最广泛的内存数据存储.相比memcached,Redis支持更丰富的数据结 ...

  5. Spring Boot中的注解

    文章来源:http://www.tuicool.com/articles/bQnMra 在Spring Boot中几乎可以完全弃用xml配置文件,本文的主题是分析常用的注解. Spring最开始是为了 ...

  6. Spring Boot中使用 Spring Security 构建权限系统

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,为应用系统提供声明式的安全 ...

  7. Spring Boot:Spring Boot 中 Redis 的使用

    Redis 介绍 Redis 是目前业界使用最广泛的内存数据存储.相比 Memcached,Redis 支持更丰富的数据结构,例如 hashes, lists, sets 等,同时支持数据持久化.除此 ...

  8. (转)Spring Boot(三):Spring Boot 中 Redis 的使用

    http://www.ityouknow.com/springboot/2016/03/06/spring-boot-redis.html Spring Boot 对常用的数据库支持外,对 Nosql ...

  9. Spring Boot(三):Spring Boot 中 Redis 的使用

    Spring Boot 对常用的数据库支持外,对 Nosql 数据库也进行了封装自动化. Redis 介绍 Redis 是目前业界使用最广泛的内存数据存储.相比 Memcached,Redis 支持更 ...

随机推荐

  1. WPF 使用Propereties:Resources.resx里面的资源

    <Window x:Class="Wpf180706.Window7"        xmlns="http://schemas.microsoft.com/win ...

  2. SICP 1.29-1.33

    1.29 (define (sum term a next b) (if (> a b) (+ (term a) (sum term (next a) next b)))) (define (c ...

  3. C# .NET锁屏程序(顺带屏蔽任务管理器)

    原文:C# .NET锁屏程序(顺带屏蔽任务管理器) 第一步:将窗体的FormBorderStyle设置为none,WindowState设为Maximized 占据整个屏幕. 第二步:使用钩子监控全局 ...

  4. VC++ 使用预编译头

    一.使用默认的预编译头       要使用预编译头,我们必须指定一个头文件,这个头文件包含我们不会经常改变的代码和其他的头文件,然后我们用这个头文件来生成一个预编译头文件(.pch文件),想必大家都知 ...

  5. 【C#】VS2012+InstallShield2013制作软件更新包

    原文:[C#]VS2012+InstallShield2013制作软件更新包 上篇文章介绍了如何使用installshield制作软件的安装包,见地址:http://blog.csdn.net/cat ...

  6. C# GetFiles

    var path = AppDomain.CurrentDomain.BaseDirectory + "Images\\Rooms\\"; // string[] patterns ...

  7. Android零基础入门第38节:初识Adapter

    原文:Android零基础入门第38节:初识Adapter 在上一节一起了解了ListView的简单使用,那么本节继续来学习与ListView有着千丝万缕的Adapter. 一.了解MVC模式 在开始 ...

  8. C++虚函数表解析(图文并茂,非常清楚)( 任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法)good

    C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有“多种形态”,这是一种泛型技术 ...

  9. shell多线程之进程间通信(2)

    工作中往往遇到这种情况,有许多任务,依次执行比较浪费时间,由于任务之间有依赖关系,简单的并发执行又不行. 就如同下面这种情况,任务new和dvidUser是可以并发执行的,fact任务依赖于new任务 ...

  10. epoll模型的探索与实践

    我们知道nginx的效率非常高,能处理上万级的并发,其之所以高效离不开epoll的支持, epoll是什么呢?,epoll是IO模型中的一种,属于多路复用IO模型; 到这里你应该想到了,select, ...