分布式锁解决方案_Zookeeper分布式锁原理

news2024/11/24 4:01:59

 

通过召zk实现分布式锁可靠性时最高的

公平锁和可重入锁的原理

取水秩序:

(1)取水之前,先取号;

(2)号排在前面的,就可以先取水;

(3)先到的排在前面,那些后到的,一个一个挨着,在井边排成一队。

 

公平锁

这种排队取水模型,就是一种锁的模型。

什么是可重入锁呢?

 

可重入锁

Zookeeper 的节点 Znode 有四种类型

持久节点:默认的节点类型。创建节点的客户端与 zookeeper 断开连接后,该节点依旧存在。

持久节点顺序节点:所谓顺序节点,在创建节点时,Zookeeper根据创建的时间顺序给该节点 名称进行编号,持久节点顺序节点就是有顺序的持久节点。

临时节点:和持久节点相反,当创建节点的客户端与 zookeeper 断开连接后,临时节点会被删除。

临时顺序节点:有顺序的临时节点。

创建临时顺序节点:create -e  -s /test 123

注意:

-e:临时节点

-s:顺序节点

 

 

创建临时顺序节点/test

 

ZK分布式锁的实现原理

当第一个客户端请求过来时,Zookeeper 客户端会创建一个持久节 点 locks。如果它(Client1)想获得锁,需要在 locks 节点下创建 一个顺序节点 lock1。

接着,客户端 Client1 会遍历查找 locks 下面的所有临时顺序子节点,判断自己的节点 lock1 是不是排序最小的那一个,如果是,则成功获 得锁。

 这时候如果又来一个客户端 client2 前来尝试获得锁,它会在 locks 下再创建一个临时节点 lock2。看看是否排在最小的那个

 客户端 client2 一样也会查找 locks 下面的所有临时顺序子节点,判 断自己的节点 lock2 是不是最小的,此时,发现 lock1 才是最小 的,于是获取锁失败。获取锁失败,它是不会甘心的,client2 向它排序靠前的节点 lock1 注册 Watcher 事件,用来监听 lock1 是否存在,也就是说client2抢锁失败进入等待状态。

 此时,如果再来一个客户端Client3来尝试获取锁,它会在 locks 下 再创建一个临时节点 lock3。

 同样的,client3 一样也会查找 locks 下面的所有临时顺序子节点, 判断自己的节点 lock3 是不是最小的,发现自己不是最小的,就获 取锁失败。它也是不会甘心的,它会向在它前面的节点 lock2 注册 Watcher 事件,以监听 lock2 节点是否存在。

释放锁

如果是任务完成,Client1 会显式调用删除 lock1 的指令。

 

 如果是客户端故障了,根据临时节点得特性,lock1 是会自动删除 的。

lock1 节点被删除后,Client2 可开心了,因为它一直监听着 lock1。lock1 节点删除,Client2立刻收到通知,也会查找locks下面的所有临时顺序子节点,发下 lock2 是最小,就获得锁。

 

 

同理,Client2 获得锁之后,Client3 也对它虎视眈眈:

Zookeeper 设计定位就是分布式协调,简单易用。如果获取不到锁,只需添加一个监听器即可,很适合做分布式锁。

Zookeeper 作为分布式锁也缺点:如果有很多的客户端频繁的申请加锁、释放锁,对于 Zookeeper 集群的压力会比较大。

分布式锁解决方案_基于Zookeeper实现分布式锁

 

简介:

Apache Curator是一个比较完善的ZooKeeper客户端框架,通过封 装的一套高级API 简化了ZooKeeper的操作。

Curator主要解决了三类问题:

封装ZooKeeper client与ZooKeeper server之间的连接处理。

提供了⼀套Fluent风格的操作API

提供ZooKeeper各种应用场景(比如:分布式锁服务、集群领导选举、共享计数器、缓存机制、分布式队列等)的抽象封装。

Curator主要从以下几个方面降低了zk使用的复杂性

重试机制:提供可插拔的重试机制, 它将给捕获所有可恢复的异常配置⼀个重试策略,并且内部也 提供了几种标准的重试策略(比如指数补偿)。

连接状态监控:Curator初始化之后会⼀直对zk连接进⾏监听,⼀旦发现连接状态发⽣变化将会作 出相应的处理。

zk客户端实例管理:Curator会对zk客户端到server集群的连接进⾏管理,并在需要的时候重建zk 实例,保证与zk集群连接的可靠性。

各种使用场景支持:Curator实现了zk支持的大部分使⽤场景(甚至包括zk自身不支持的场景), 这些实现都遵循了zk的最佳实践,并考虑了各种极端情况。

在项目的pom.xml中引入Curator依赖

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>5.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>5.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-client</artifactId>
    <version>5.2.0</version>
</dependency>

在项目中创建包config,并创建配置类ZookeeperConfig

package com.ss.demo.config;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ZookeeperConfig {
    /**
     * 创建Curator的客户端
     * @return
     */
    @Bean
    public CuratorFramework zookeeperClient() {
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("127.0.0.1:2181")  //zk的连接地址
                .sessionTimeoutMs(5000)            //会话的超时时间默认为6秒
                .connectionTimeoutMs(5000)         //连接创建的超时时间
                .retryPolicy(new ExponentialBackoffRetry(1000, 3))  //连接的重试次数【1秒重试3次】
                .build();
        client.start();    //启动即可
        return client;
    }
}

修改service中的接口:ITOrderService

package com.ss.demo.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.ss.demo.domain.TOrder;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author laozhang
 * @since 2023-04-04
 */
public interface ITOrderService extends IService<TOrder> {
    /**
     * 创建订单方法
     * @param productId
     * @param count
     * @return
     */
    String createOrder(Integer productId, Integer count);

    /**
     * 使用悲观锁进行实现
     * @param productId
     * @param count
     * @return
     */
    String createOrderPessimisticlock(Integer productId, Integer count);

    /**
     * 乐观锁
     * @param productId
     * @param count
     * @return
     */
    String createOrderOptmisticlock(Integer productId, Integer count);

    /**
     * Redis操作
     * @param productId
     * @param count
     * @return
     */
    String createOrderRedis(Integer productId, Integer count);

    /**
     * Redisson操作
     * @param productId
     * @param count
     * @return
     */
    String createOrderRedisson(Integer productId, Integer count);

    /**
     * zookeeper操作
     * @param productId
     * @param count
     * @return
     */
    String createOrderZookeeper(Integer productId, Integer count);
}

修改service实现类:ITOrderServiceImpl

@Autowired
private CuratorFramework curatorFramework;

/**
 * zookeeper操作
 * @param productId
 * @param count
 * @return
 */
@Override
public String createOrderZookeeper(Integer productId, Integer count) throws Exception {
    //创建锁【注这里是公平锁】
    InterProcessMutex lock = new InterProcessMutex(curatorFramework, "/lockPath");

    //尝试获得锁,我们在这里可以等待5秒钟
    if(lock.acquire(5,TimeUnit.SECONDS)) {  //第一个参数为时间,第二个参数为时间的单位
        try {
            //根据商品id获取商品信息
            Product product = productMapper.selectById(productId);
            if(product == null) {
                throw new RuntimeException("购买商品不存在");
            }
            log.info(Thread.currentThread().getName() +"库存数量" + product.getCount());
            //校验库存
            if(count > product.getCount()) {
                throw new RuntimeException("库存不足");
            }
            //更新库存
            Integer iCount = product.getCount() - count;
            product.setCount(iCount);
            //更新操作
            productMapper.updateById(product);
            //创建订单操作
            TOrder order = new TOrder();
            order.setOrderStatus(1);
            order.setReceiverName("张三");
            order.setReceiverMobile("12345678765");
            //设置订单价格【商品单价*商品数量】
            order.setOrderAmount(product.getPrice().multiply(new BigDecimal(count)));
            orderMapper.insert(order);  //插入订单操作
            //创建订单商品表的操作
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderId(order.getId());     //订单Id
            orderItem.setProduceId(product.getId());  //商品Id
            orderItem.setPurchasePrice(product.getPrice()); //购买价格
            orderItem.setPurchaseNum(count);   //购买数量
            orderItemMapper.insert(orderItem);
            return order.getId();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.release();  //释放锁
        }
    }
    return "创建失败";
}

修改controller:TOrderController

package com.ss.demo.controller;
import com.ss.demo.service.ITOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 *  前端控制器
 * </p>
 */
@RestController
@RequestMapping("/order")
public class TOrderController {
    @Autowired
    private ITOrderService orderService;

    @PostMapping("/create")
    public String createOrder(Integer productId, Integer count) throws Exception {
        //return orderService.createOrder(productId, count);
        //return orderService.createOrderRedis(productId,count);
        //return orderService.createOrderRedisson(productId, count);
        return orderService.createOrderZookeeper(productId, count);
    }
}

启动服务9091,9090

使用Jmeter记性测试

把数据库所有数据复原

测试略过

三种分布式锁对比

数据库分布式锁实现

优点:

简单,

使用方便,

不需要引入 Redis、Zookeeper 等中间 件。

缺点:

不适合高并发的场景

db 操作性能较差

Redis 分布式锁实现

优点:

性能好,适合高并发场景

较轻量级

有较好的框架支持,如 Redisson

缺点:

过期时间不好控制

需要考虑锁被别的线程误删场景

Zookeeper 分布式锁实现

优点:

有较好的性能和可靠性

有封装较好的框架,如 Curator

缺点:

性能不如 Redis 实现的分布式锁

比较重的分布式锁。

汇总对比:

从性能角度:Redis > Zookeeper >= 数据库

从实现的复杂性角度:Zookeeper > Redis > 数据库

从可靠性角度:Zookeeper > Redis > 数据库

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/540331.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Go语言的学习【1】基础语法之前的准备事项

目录 什么是云原生学习方法go语言的IDE配置之VScode写go代码要注意的事情一些基本命令基础语法Go 语言原生自带测试Go vetPrint-format 错误&#xff0c;检查类型不匹配的printBoolean 错误&#xff0c;检查一直为 true、false 或者冗余的表达式Range 循环&#xff0c;比如如下…

ThingsBoard部署tb-gateway并配置OPCUA

1、安装 我实在自己的虚拟机上安装,使用官方Docker的安装方式 docker run -it -v ~/.tb-gateway/logs:/thingsboard_gateway/logs -v ~/.tb-gateway/extensions:/thingsboard_gateway/extensions -v ~/.tb-gateway/config:/thingsboard_gateway/config --name tb-gateway --…

《安富莱嵌入式周报》第312期:开源磁场照相机,仿生神经元PCB,开源无线耳机,手机系统PalmOS移植到各种单片机,开放系统组装协议OSAP

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 更新一期视频教程&#xff1a; BSP视频教程第26期&#xff1a;CAN/CANFD/CANopen专题&#xff0c;CANFD整个运行机制精…

PyTorch RNN的原理及其手写复现。

PyTorch RNN的原理及其手写复现。 记忆单元(考虑过去的信息)分类包括&#xff1a;1.RNN 2.GRU 3.LSTM模型类别&#xff1a;1.单向循环(左到右) 2.双向循环&#xff08;考虑未来信息&#xff09; 3.多层单向或双向循环优缺点应用场景具体公式 代码实现 记忆单元(考虑过去的信息)…

网络安全合规-数据安全评估

目前&#xff0c;我国在数据管理领域&#xff0c;已经正式出台的国家标准有《数据管理能力成熟度评估模型&#xff08;GB/T 36073-2018&#xff09;》&#xff08;DCMM&#xff09;&#xff0c;在数据安全检测评估、认证领域的标准有《数据安全能力成熟度模型&#xff08;GB/T …

实现取关和关注功能

将关注过的用户id存如数据库中 //关注或者取关 Override public Result follow(Long id, Boolean flag) { //1.获取当前登录用户的id UserDTO user UserHolder.getUser(); if(usernull){ return Result.fail("请先登录"); } Long userId user.getId(); //2.判断是关…

vue3+antDesignVue前端纯导出

效果 <a-buttonsize"default"style"margin-left: 10px"click"exportData">导出</a-button>1.下载所需依赖 npm install xlsx --save npm install file-saver --save<script setup> import { reactive, ref } from "vue…

SpringBoot——多环境开发

简单介绍&#xff1a; 在我们的开发过程中&#xff0c;我们的程序开发分为几个基本的阶段&#xff0c;比如开发阶段&#xff0c;调试阶段&#xff0c;运行阶段&#xff0c;在不同的阶段可能需要有不同的配置文件去对我们的项目做配置&#xff0c;那么要如何在不同的环境中配置…

C语言:数组定义方式

一、数组简介 <1>前言 大家首先来思考一个问题&#xff0c;若是我们想要定义两个变量&#xff0c;求这两个数的平均数&#xff0c;该怎么求呢&#xff1f; 例如&#xff1a;int a 10,b 20 int average (a b) / 2; 上面的公式&#xff0c;我相信大家应该很快就能够求出…

牛奶蛋白过敏VS乳糖不耐受,看完这篇终于分清了

乳糖不耐受 乳糖不耐症的主要表现为一些胃肠道症状&#xff0c;有腹部紧张、肠鸣、腹痛、排气和腹泻等症状。如出现这些症状都是乳糖不耐受症的表现之一&#xff0c;但患有乳糖不耐的个体症状差异很大&#xff0c;而严重的乳糖不耐受多于摄入一定量乳糖后30min至数小时内发生。…

Python安装教程2023(小白专用)已验证

第一次接触Python&#xff1f;下面从一个完全不懂的Python 的小白来安装Python 等一系列工作的记录&#xff0c;并且遇到的问题也会写出&#xff0c;让完全不懂的小白也可上手安装&#xff0c;并且完成第一个Hello world代码。 [Python 安装] 目前&#xff0c;Python有两个版本…

【手机建站】Android Termux+cpolar内网穿透,搭建外网可以访问的网站

文章目录 概述1.搭建apache2.安装cpolar内网穿透3.公网访问配置4.固定公网地址5.添加站点 概述 Termux是一个Android终端仿真应用程序&#xff0c;用于在 Android 手机上搭建一个完整的Linux 环境&#xff0c;能够实现Linux下的许多基本操作&#xff0c;不需要root权限Termux就…

Word怎么转换成PDF免费?分享适合你的Word转PDF方法

随着数字化时代的到来&#xff0c;将文件转换为PDF格式已经成为一个常见的需求。PDF文件格式的广泛应用使其在各个领域都非常重要&#xff0c;而Word文档则是最常见的文件类型之一。因此&#xff0c;将Word转换为PDF的方法备受关注。在下面&#xff0c;我将分享一种适合任何人使…

基于ARIMA-CNN-LSTM预测模型研究(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

大数据服务之linux中安装hadoop

安装hadoop之前&#xff0c;必须要先有jdk。所以jdk 在linux环境中配置java全局变量 第一步&#xff0c;下载 下载地址&#xff1a;Index of /dist/hadoop/common/hadoop-3.1.3 第二步&#xff0c;解压 tar -zxvf hadoop-3.1.3.tar.gz 第三步&#xff0c;获取文件路径 pw…

站的更高一点看分布式系统中的复制

复制意味着在通过网络连接的多台机器上保留相同数据的副本。 我们希望能复制数据&#xff0c;可能出于各种各样的原因&#xff1a; 使得数据与用户在地理上接近&#xff08;从而减少延迟&#xff09;即使系统的一部分出现故障&#xff0c;系统也能继续工作&#xff08;从而提…

网络带宽管理

网络某一部分的带宽使用过多&#xff0c;可能会影响整个网络的性能&#xff0c;带宽问题甚至会影响业务关键型服务并导致网络停机。在企业中保持稳定的网络性能可能具有挑战性&#xff0c;因为采用数字化的网络可扩展性和敏捷性应该与组织的发展同步。随着基础设施的扩展、新应…

flask+p5.js 网页开发 pycharm mac环境

flask 学习参考视频&#xff1a; 2023版-零基础玩转Python Flask框架-学完可就业 一、安装 需要安装的&#xff1a; python、flask、pycharm mysql、Navicat&#xff08;这两个不用数据库可以不用安装&#xff09; 1、python3.0以上安装 下载地址 下载以后终端输入python3 …

Vue入门学习笔记(1)

这里写目录标题 一、安装工具二、前置设置2.1 淘宝加速2.2、华为云加速 三、安装vue工具3.1 安装vue/cli3.2 安装vite 四、创建项目4.1 使用vue/cli创建4.2 使用vite创建 一、安装工具 首先须安装以下工具&#xff1a; vscode&#xff08;如果下载慢的话建议使用360安装&…

select

1. select模型 2. select()函数 int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);fd_set类型 readfds和writefds, exceptfds的类型都是fd_set,那么fd_set类型是什么呢&#xff1f; fd_set类型本质是一个位图&#xff0…