From 29a6cc946b8356054a77f4eb8d84b5f08cbb14fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BD=97=E7=A5=A5?= <1366971433@qq.com> Date: Mon, 4 May 2020 22:07:24 +0800 Subject: [PATCH] =?UTF-8?q?Java=5F=E5=87=BD=E6=95=B0=E5=BC=8F=E7=BC=96?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SREADME.md | 183 +++++++ code/Java/stream-tutorial/pom.xml | 54 ++ .../java/com/heibaiying/DistributedLock.java | 41 ++ .../main/java/com/heibaiying/Employee.java | 70 +++ .../main/java/com/heibaiying/StreamTest.java | 62 +++ .../src/main/resources/log4j.properties | 7 + notes/Https.md | 40 ++ notes/Java_函数式编程.md | 510 ++++++++++++++++++ notes/Redis_分布式锁原理.md | 322 +++++++++++ notes/Redis_持久化.md | 16 +- notes/ZooKeeper_分布式锁原理.md | 357 ++++++++++++ pictures/redis_分布式锁_cli1.png | Bin 0 -> 8029 bytes pictures/redis_分布式锁_cli2.png | Bin 0 -> 6007 bytes pictures/redis_分布式锁_cli3.png | Bin 0 -> 19433 bytes pictures/redis_分布式锁原理.png | Bin 0 -> 30785 bytes pictures/zookeeper_分布式读写锁.png | Bin 0 -> 11198 bytes pictures/zookeeper_分布式锁_cli.png | Bin 0 -> 15271 bytes ..._分布式锁_临时有序节点方案.png | Bin 0 -> 15043 bytes ...keeper_分布式锁_临时节点方法.png | Bin 0 -> 13362 bytes 19 files changed, 1654 insertions(+), 8 deletions(-) create mode 100644 SREADME.md create mode 100644 code/Java/stream-tutorial/pom.xml create mode 100644 code/Java/stream-tutorial/src/main/java/com/heibaiying/DistributedLock.java create mode 100644 code/Java/stream-tutorial/src/main/java/com/heibaiying/Employee.java create mode 100644 code/Java/stream-tutorial/src/main/java/com/heibaiying/StreamTest.java create mode 100644 code/Java/stream-tutorial/src/main/resources/log4j.properties create mode 100644 notes/Https.md create mode 100644 notes/Java_函数式编程.md create mode 100644 notes/Redis_分布式锁原理.md create mode 100644 notes/ZooKeeper_分布式锁原理.md create mode 100644 pictures/redis_分布式锁_cli1.png create mode 100644 pictures/redis_分布式锁_cli2.png create mode 100644 pictures/redis_分布式锁_cli3.png create mode 100644 pictures/redis_分布式锁原理.png create mode 100644 pictures/zookeeper_分布式读写锁.png create mode 100644 pictures/zookeeper_分布式锁_cli.png create mode 100644 pictures/zookeeper_分布式锁_临时有序节点方案.png create mode 100644 pictures/zookeeper_分布式锁_临时节点方法.png diff --git a/SREADME.md b/SREADME.md new file mode 100644 index 0000000..4e67999 --- /dev/null +++ b/SREADME.md @@ -0,0 +1,183 @@ +# Full-Stack-Notes + +
+
+ +
+ + 一个处于萌芽阶段的知识库,用于持续分享自己的所见、所学、所思! +
+
+ +
+ 点击切换详细目录 +
+ +## :coffee: JAVA + +1. [Java 反射与注解](notes/Java_反射与注解.md) +2. [Java 并发编程](notes/Java_并发编程.md) +3. [Java 设计模式](notes/Java_设计模式.md) +4. [Java 虚拟机](notes/Java_虚拟机.md) +5. [JVM 性能监控之命令行工具](notes/JVM_性能监控之命令行工具.md) +6. [JVM 性能监控之可视化工具](notes/JVM_性能监控之可视化工具.md) +7. [Java NIO 核心组件详解](notes/Java_NIO.md) +8. 函数式编程 +9. [Tomcat 架构解析](notes/Tomcat_架构解析.md) +10. Java 集合类源码解析 + +
+ +## :globe_with_meridians: 计算机与网络基础 + +1. [计算机网络模型](notes/计算机网络.md) + +2. HTTP 协议详解 + +3. HTTPS 协议详解 + +4. 抓包神器 Wireshark + +5. 计算机组成原理 + +
+ +## :computer: 前端基础 + +1. [JavaScript 基础](notes/JavaScript_基础.md) + +2. [ECMAScript 6.0 基础](notes/ES6_基础.md) + +3. CSS 基础 + +4. JavaScript 设计模式 + +
+ +## :dolphin: 数据库 + +### MySQL + +1. [MySQL 核心概念](notes/MySQL_基础.md) + +2. [MySQL 备份详解](notes/MySQL_备份.md) + +3. [MySQL 复制详解](notes/MySQL_复制.md) + +4. [MySQL 高可用架构之 PXC 集群](notes/MySQL_PXC集群.md) + +5. [MyCat 读写分离与分库分表](notes/MySQL_Mycat中间件.md) + +6. [MySQL 查询性能分析之 Explain](notes/MySQL_EXPLAIN.md) + +### Redis + +1. [Redis 基本数据类型和常用命令](notes/Redis_数据类型和常用命令.md) + +2. [Redis AOF 和 RDB 持久化策略原理](notes/Redis_持久化.md) + +3. [Redis 哨兵模式](notes/Redis_哨兵模式.md) + +4. [Redis 集群模式](notes/Redis_集群模式.md) + +5. 使用 Redis 实现分布式锁 + + +### MongoDB + +1. [MongoDB 基础](notes/MongoDB_基础.md) + +2. [MongoDB 索引](notes/MongoDB_索引.md) + +3. [MongoDB 聚合](notes/MongoDB_聚合.md) + +4. [MongoDB 复制](notes/MongoDB_复制.md) + +5. [MongoDB 分片](notes/MongoDB_分片.md) + +
+ +## :whale: 系统与容器 + +1. [Linux 常用 Shell 命令](notes/Linux_常用Shell命令.md) + +2. [Sehll 脚本编程基础](notes/Shell_基础.md) + +3. [Docker 基础](notes/Docker_基础.md) + +
+ +## :package: 常用技术栈 + + +### RabbitMQ + +1. [RabbitMQ 核心概念](notes/RabbitMQ_基础.md) + +2. [RabbitMQ 客户端开发](notes/RabbitMQ_客户端开发.md) + +3. [HAProxy + KeepAlived 搭建 RabbitMQ 高可用集群](notes/RabbitMQ_高可用集群架构.md) + +### Nginx + +1. [Nginx 基础之静态网站部署,负载均衡,动静分离](notes/Nginx_基础.md) +2. HTTP 模块详解 +3. Nginx 性能优化 + + +### Kafka + +1. [Kafka 简介](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Kafka简介.md) +2. [基于 Zookeeper 搭建 Kafka 高可用集群](https://github.com/heibaiying/BigData-Notes/blob/master/notes/installation/基于Zookeeper搭建Kafka高可用集群.md) +3. [Kafka 生产者详解](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Kafka生产者详解.md) +4. [Kafka 消费者详解](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Kafka消费者详解.md) +5. [深入理解 Kafka 副本机制](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Kafka深入理解分区副本机制.md) + + +### ZooKeeper + +1. [ZooKeeper 简介及核心概念](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Zookeeper简介及核心概念.md) +2. [ZooKeeper 单机环境和集群环境搭建](https://github.com/heibaiying/BigData-Notes/blob/master/notes/installation/Zookeeper单机环境和集群环境搭建.md) +3. [ZooKeeper 常用 Shell 命令](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Zookeeper常用Shell命令.md) +4. [ZooKeeper Java 客户端](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Zookeeper_Java客户端Curator.md) +5. [ZooKeeper ACL 权限控制](https://github.com/heibaiying/BigData-Notes/blob/master/notes/Zookeeper_ACL权限控制.md) +6. 使用 ZooKeeper 实现分布式锁 + +## ElasticSearch + +TODO + +
+ +## :rocket: 测试与运维 + +1. 性能测试之 Jmeter +2. 性能测试之 LoadRunner +3. Jenkins 持续交付与自动化部署 + +
+ +## :bullettrain_side: 微服务与分布式 + +1. 分布式锁的实现 +2. 分布式选举算法 +3. 分布式事务实现原理 +4. 分布式全局 ID 的生成 +5. CAP 理论和 BASE 理论 + +
+ +## :hammer_and_wrench: 常用软件安装 + +1. [Redis 单机环境安装](notes/installation/Redis单机环境搭建.md) +2. [RabbitMQ 单机环境安装](notes/installation/RabbitMQ单机环境搭建.md) +3. [Nginx 单机环境安装](notes/installation/Nginx编译方式安装.md) +4. [MySQL 单机环境安装](notes/installation/MySQL单机环境搭建.md) +5. [MongoDB 单机环境安装](notes/installation/MongoDB单机环境搭建.md) +6. [ElasticSearch + Kibana 单机环境安装](notes/installation/ElasticSearch+Kibana单机环境搭建.md) + +
+ +
+ +
欢迎关注我的博客:https://blog.csdn.net/m0_37809146
diff --git a/code/Java/stream-tutorial/pom.xml b/code/Java/stream-tutorial/pom.xml new file mode 100644 index 0000000..9824945 --- /dev/null +++ b/code/Java/stream-tutorial/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + org.example + stream-tutorial + 1.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + + + + + + + org.redisson + redisson + 3.12.5 + + + + redis.clients + jedis + 3.2.0 + + + org.apache.curator + curator-framework + 4.3.0 + + + org.apache.curator + curator-recipes + 4.3.0 + + + org.apache.zookeeper + zookeeper + 3.4.14 + + + + \ No newline at end of file diff --git a/code/Java/stream-tutorial/src/main/java/com/heibaiying/DistributedLock.java b/code/Java/stream-tutorial/src/main/java/com/heibaiying/DistributedLock.java new file mode 100644 index 0000000..81bf86a --- /dev/null +++ b/code/Java/stream-tutorial/src/main/java/com/heibaiying/DistributedLock.java @@ -0,0 +1,41 @@ +package com.heibaiying; + +import org.apache.curator.RetryPolicy; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.recipes.locks.InterProcessMutex; +import org.apache.curator.retry.RetryNTimes; + +import java.util.concurrent.TimeUnit; + +public class DistributedLock { + + + public static void main(String[] args) throws Exception { + + + RetryPolicy retryPolicy = new RetryNTimes(3, 5000); + CuratorFramework client = CuratorFrameworkFactory.builder() + .connectString("192.168.0.105:2181") + .sessionTimeoutMs(10000).retryPolicy(retryPolicy) + .namespace("mySpace").build(); + client.start(); + + // 1. 创建分布式锁 + InterProcessMutex lock = new InterProcessMutex(client, "/distributed/myLock"); + + // 2.尝试获取分布式锁 + if (lock.acquire(10, TimeUnit.SECONDS)) { + try { + System.out.println("模拟业务耗时"); + Thread.sleep(3 * 1000); + } finally { + // 3.释放锁 + lock.release(); + } + } + + client.close(); + } + +} diff --git a/code/Java/stream-tutorial/src/main/java/com/heibaiying/Employee.java b/code/Java/stream-tutorial/src/main/java/com/heibaiying/Employee.java new file mode 100644 index 0000000..773ebca --- /dev/null +++ b/code/Java/stream-tutorial/src/main/java/com/heibaiying/Employee.java @@ -0,0 +1,70 @@ +package com.heibaiying; + +public class Employee { + private String name; + private String gender; + private String company; + private int age; + private boolean isOfficial; + + public Employee(String name, String gender, String company, int age) { + this.name = name; + this.gender = gender; + this.company = company; + this.age = age; + } + + Employee(String name, int age,boolean isOfficial) { + this.name = name; + this.age = age; + this.isOfficial = isOfficial; + } + + @Override + public String toString() { + return "Employee{" + + "name='" + name + '\'' + + '}'; + } + + public boolean isOfficial() { + return isOfficial; + } + + public void setOfficial(boolean official) { + isOfficial = official; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + + public String getCompany() { + return company; + } + + public void setCompany(String company) { + this.company = company; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + +} diff --git a/code/Java/stream-tutorial/src/main/java/com/heibaiying/StreamTest.java b/code/Java/stream-tutorial/src/main/java/com/heibaiying/StreamTest.java new file mode 100644 index 0000000..dad7c13 --- /dev/null +++ b/code/Java/stream-tutorial/src/main/java/com/heibaiying/StreamTest.java @@ -0,0 +1,62 @@ +package com.heibaiying; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.function.BinaryOperator; +import java.util.function.IntConsumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class StreamTest { + public static void main(String[] args) { + System.out.println(UUID.randomUUID() + ":" + Thread.currentThread().getId()); + + } + + /** + * 进行求和 + * + * @param list + * @param initValue + * @param binaryOperator + * @param + * @return + */ + public static T reduce(List list, T initValue, BinaryOperator binaryOperator) { + for (T t : list) { + initValue = binaryOperator.apply(initValue, t); + } + return initValue; + } + + /** + * 集合过滤 + * + * @param list 待过滤的集合 + * @param predicate 函数式接口 + * @param 集合中元素的类型 + * @return 满足条件的元素的集合 + */ + public static List filter(List list, CustomPredicate predicate) { + ArrayList result = new ArrayList<>(); + for (T t : list) { + if (predicate.test(t)) result.add(t); + } + return result; + } + + + /** + * 定义接口 + * + * @param 参数类型 + */ + @FunctionalInterface + public interface CustomPredicate { + // 判断是否满足过滤标准 + boolean test(T t); + } + +} diff --git a/code/Java/stream-tutorial/src/main/resources/log4j.properties b/code/Java/stream-tutorial/src/main/resources/log4j.properties new file mode 100644 index 0000000..b5451a7 --- /dev/null +++ b/code/Java/stream-tutorial/src/main/resources/log4j.properties @@ -0,0 +1,7 @@ +log4j.rootLogger=INFO, SYSLOG + +log4j.appender.SYSLOG=org.apache.log4j.net.SyslogAppender +log4j.appender.SYSLOG.syslogHost=127.0.0.1 +log4j.appender.SYSLOG.layout=org.apache.log4j.PatternLayout +log4j.appender.SYSLOG.layout.conversionPattern=%d{ISO8601} %-5p [%t] %c{2} %x - %m%n +log4j.appender.SYSLOG.Facility=LOCAL1 \ No newline at end of file diff --git a/notes/Https.md b/notes/Https.md new file mode 100644 index 0000000..2317720 --- /dev/null +++ b/notes/Https.md @@ -0,0 +1,40 @@ +# HTTPS + +## 一、核心概念 + +### 1.1 SSL + +安全套接层(英语:Secure Sockets Layer,缩写:SSL)是一种安全协议,目的是为互联网通信提供安全保障,最早由网景公司(Netscape)推出。SSL 协议有三个版本,分别是 SSL v1、SSL v2、SSL v3: + +- v1 版本从未公开过,因为存在严重的安全漏洞。 +- v2 版本在1995年2月发布,但因为存在多个严重的安全漏洞而被 v3 版本替代。 +- v3 版本在1996年发布,是由网景公司完全重新设计的。 + +### 1.2 TLS + +1966 年,TETF(Internet Engineering Task Force)组织在 SLL v3 的基础进一步进行了标准化,微软为这个新的协议取名为 TLS v1.0,这也就是TLS(Transport Layer Security)的由来。之后 TLS 继续发布了 v1.1,v1.2,v1.3 版本协议,当前主流的版本为 v1.2。 + +### 1.3 OpenSSL + +OpenSSL 是一个开源的底层密码库,封装了所有的密码学算法,并为 TLS/SSL 提供了功能完善的工具库,因此它是 TLS/SSL 协议的具体实现。 + +### 1.4 HTTPS + +HTTPS (Hyper Text Transfer Protocol over SecureSocket Layer)是在 HTTP 的基础上通过 SSL/TLS 层来进行传输加密和身份认证,进而保证通讯的安全性。除此之外它的报文结构、请求方法、连接管理等都完全沿用 HTTP 原有的模式,因此可以很方便地将原有 HTTP 服务转换为 HTTPS 服务。 + + + +## 二、数据安全 + +HTTPS 的数据安全主要是通过 SSL/TLS 协议来进行实现的,SSL/TLS 则主要采用了以下方式来保证传输的安全: + +### 2.1 非对称加密 + + + +### 2.2 对称加密 + + + +## 三、握手过程 + diff --git a/notes/Java_函数式编程.md b/notes/Java_函数式编程.md new file mode 100644 index 0000000..44a0aa7 --- /dev/null +++ b/notes/Java_函数式编程.md @@ -0,0 +1,510 @@ +# Java 函数式编程 + +## 一、Lambda + +### 1.1 格式 + +Java 从 1.8 版本开始支持 Lambda 表达式,通过 Lambda 表达式我们可以将一个函数作为参数传入方法中。在 JDK 1.8 之前,我们只能通过匿名表达式来完成类似的功能,但是匿名表达式比较繁琐,存在大量的冗余代码,不利于将行为参数化,而采用 Lamdba 则能很好的解决这个问题。Lambda 表达式的基本语法如下: + +```java +(parameters) -> expression +``` + +或采用花括号的形式: + +```java +(parameters) -> { statements; } +``` + +Lambda 表达式具有如下特点: + +- **可选的参数:**不需要声明参数类型,编译器会从上下文自动进行推断; +- **可选的参数圆括号:**当且仅当只有一个参数时,圆括号可以省略; +- **可选的花括号:**如果主体只有一个表达式,则无需使用花括号; +- **可选的返回关键字:**如果主体只有一个表达式,则该表达式的值就是整个 Lambda 表达式的返回值,此时不需要使用 return 关键字进行显式的返回。 + +### 1.2 行为参数化 + + 上面我们说过,Lambda 表达式主要解决的是行为参数化的问题,而什么是行为参数化?下面给出一个具体的示例: + +```java +/** + * 定义函数式接口 + * @param 参数类型 + */ +@FunctionalInterface +public interface CustomPredicate { + boolean test(T t); +} +``` + +```java +/** + * 集合过滤 + * @param list 待过滤的集合 + * @param predicate 函数式接口 + * @param 集合中元素的类型 + * @return 满足条件的元素的集合 + */ +public static List filter(List list, CustomPredicate predicate) { + ArrayList result = new ArrayList<>(); + for (T t : list) { + // 将满足条件的元素添加到返回集合中 + if (predicate.test(t)) result.add(t); + } + return result; +} + +``` + +针对不同类型的集合,我们可以通过传入不同的 Lambda 表达式作为参数来表达不同的过滤行为,这就是行为参数化: + +```java +List integers = Arrays.asList(1, 2, 3, 4, 5); +filter(integers, x -> x % 2 == 0); // 过滤出所有偶数 + +List employees = Arrays.asList( + new Employee("张某", 21, true), + new Employee("李某", 30, true), + new Employee("王某", 45, false)); +filter(employees, employee -> employee.getAge() > 25); // 过滤出所有年龄大于25的员工 +``` + +需要注意的是上面我们声明接口时,使用了 `@FunctionalInterface` 注解,它表示当前的接口是一个函数式接口。函数式接口就是只含有一个抽象方法的接口;即一个接口不论含有多少个默认方法和静态方法,只要它只有一个抽象方法,它就是一个函数式接口。使用 `@FunctionalInterface` 修饰后,当该接口有一个以上的抽象方法时,编译器就会进行提醒。 + +任何使用到函数式接口的地方,都可以使用 Lambda 表达式进行简写。例如 Runnable 接口就是一个函数式接口,我们可以使用 Lambda 表达式对其进行简写: + +```java +new Thread(() -> { + System.out.println("hello"); +}); +``` + +### 1.3 方法引用和构造器引用 + +紧接上面的例子,如果我们需要过滤出所有的正式员工,除了可以写成下面的形式外: + +```java +filter(employees, employee -> employee.isOfficial()); +``` + +还可以使用方法引用的形式进行简写: + +```java +filter(employees, Employee::isOfficial); +``` + +除了方法引用外,还可以对构造器进行引用,示例如下: + +```java +Stream stream = Stream.of(1, 3, 5, 2, 4); +stream.collect(Collectors.toCollection(ArrayList::new)); //等价于 toCollection(()->new ArrayList<>()) +``` + +方法引用和构造器引用的目的都是为了让代码更加的简洁。 + + + +## 二、函数式接口 + +通常我们不需要自定义函数式接口,JDK 中内置了大量函数式接口,基本可以满足大多数场景下的使用需求,最基本的四种如下: + +**1. Consumer\**:消费型接口,消费输入的变量,没有返回值: + +```java +@FunctionalInterface +public interface Consumer { + void accept(T t); + ... +} +``` + +**2. Consumer\**:供给型接口,供给变量: + +```java +@FunctionalInterface +public interface Supplier { + T get(); +} +``` + +**3. Function**:对输入类型为 T 的变量执行特定的转换操作,并返回类型为 R 的返回值: + +```java +@FunctionalInterface +public interface Function { + R apply(T t); + ... +} +``` + + **4. Predicate\**:判断类型为 T 的变量是否满足特定的条件,如果满足则返回 true,否则返回 flase: + +```java +@FunctionalInterface +public interface Predicate { + boolean test(T t); + ... +} +``` + +其他函数式接口都是这四种基本类型的延伸和扩展。以 BiFunction 和 BinaryOperator 接口为例: + ++ **BiFunction**:是函数型接口 Function 的扩展,Function 只能接收一个入参;而 BiFunction 可以用于接收两个不同类型的入参; ++ **BinaryOperator\**:是 BiFunction 的一种特殊化情况,即两个入参和返回值的类型均相同,通常用于二元运算: + +```java +@FunctionalInterface +public interface BiFunction { + R apply(T t, U u); +} + +@FunctionalInterface +public interface BinaryOperator extends BiFunction { + .... +} +``` + +使用示例如下: + +```java +public static void main(String[] args) { + List integers = Arrays.asList(1, 2, 3, 4, 5); + reduce(integers, 0, (a, b) -> a + b); // 求和 输出:15 + reduce(integers, 1, (a, b) -> a * b); // 求积 输出:120 +} + + +/** + * 执行归约操作 + */ +public static T reduce(List list, T initValue, BinaryOperator binaryOperator) { + for (T t : list) { + initValue = binaryOperator.apply(initValue, t); + } + return initValue; +} +``` + + + +## 三、创建流 + +JDK 1.8 中最主要的变化是引入了流,通过流、Lamda 表达式以及函数式接口,可以高效地完成数据的处理。创建流通常有以下四种方法: + +**1. 由值创建** + +使用静态方法 `Stream.of()` 由指定的值进行创建: + +```java +Stream stream = Stream.of("a", "b ", "c", "d"); +``` + +**2. 由集合或数组创建** + +使用静态方法 `Arrays.stream()` 由指定的数组进行创建: + +```java +String[] strings={"a", "b ", "c", "d"}; +Stream stream = Arrays.stream(strings); +``` + +调用集合类的 `stream()` 方法进行创建: + +```shell +List strings = Arrays.asList("a", "b ", "c", "d"); +Stream stream = strings.stream(); +``` + +`stream()` 方法定义在 `Collection` 接口中,它是一个默认方法,因此大多数的集合都可以通过该方法转换为流: + +```java +public interface Collection extends Iterable { + default Stream stream() { + return StreamSupport.stream(spliterator(), false); + } +} +``` + +**3. 由文件创建** + +```java +try (Stream lines = Files.lines(Paths.get("pom.xml"), StandardCharsets.UTF_8)) { + lines.forEach(System.out::println); +} catch (IOException e) { + e.printStackTrace(); +} +``` + +**4. 由函数创建** + +除了以上方法外,还可以通过 `Stream.iterate()` 和 `Stream.generate()` 方法来来创建无限流: + ++ `Stream.iterate()` 接受两个参数:第一个是初始值,第二个参数是一个输入值和输出值相同的函数型接口。它主要用于迭代式的产生新的元素,示例如下: + + ```java + // 依次输出1到9 + Stream.iterate(0, x -> x + 1).limit(10).forEach(System.out::print); + ``` + ++ `Stream.generate()` 接收一个供应型函数作为参数,用于按照该函数产生新的元素: + + ```java + // 依次输出随机数 + Stream.generate(Math::random).limit(10).forEach(System.out::print); + ``` + +## 四、操作流 + +### 4.1 基本操作 + +当流创建后,便可以利用 Stream 类的各种方法对其上数据进行各种处理,常用的方法如下: + +| 操作 | 作用 | 返回类型 | 使用的类型/函数式接口 | +| --------- | ------------------------------ | ------------ | ---------------------- | +| filter | 过滤符合条件的元素 | Stream\ | Predicate\ | +| distinct | 过滤重复元素 | Stream\ | | +| skip | 跳过指定数量的元素 | Stream\ | long | +| limit | 限制元素的数量 | Stream\ | long | +| map | 对元素执行特定转换操作 | Stream\ | Function | +| flatMap | 将元素扁平化后执行特定转换操作 | Stream\ | Function> | +| sorted | 对元素进行排序 | Stream\ | Comparator\ | +| anyMatch | 是否存在指定元素满足特定条件 | boolean | Predicate\ | +| noneMatch | 是否所有元素都不满足特定条件 | boolean | Predicate\ | +| allMatch | 是否所有元素都满足特定条件 | boolean | Predicate\ | +| findAny | 返回任意一个满足指定条件的元素 | Optional\ | | +| findFirst | 返回第一个满足指定条件的元素 | Optional\ | | +| forEach | 对所有元素执行特定的操作 | void | Cosumer\ | +| collect | 对所有元素指定特定的收集操作 | R | Collector | +| reduce | 对元素依次执行归约操作 | Optional\ | BinaryOperator\ | +| count | 计算流中元素的数量 | long | | + +> 注:上表中返回类型为 Stream\ 的操作都是中间操作,代表还可以继续调用其它方法对流进行处理。返回类型为其它的操作都是终止操作,代表处理过程到此为止。 + +使用示例如下: + +```java +Stream.iterate(0, x -> x + 1) // 构建流 + .limit(20) // 限制元素的个数 + .skip(10) // 跳过前10个元素 + .filter(x -> x % 2 == 0) // 过滤出所有偶数 + .map(x -> "偶数:" + x) // 对元素执行转换操作 + .forEach(System.out::println); // 打印出所有元素 +``` + +输出结果如下: + +```shell +偶数:10 +偶数:12 +偶数:14 +偶数:16 +偶数:18 +``` + + 上表的 `flatMap()` 方法接收一个参数,它是一个函数型接口 `Function> mapper`,该接口用于将流中的元素转换为 `Stream` ,从而可以将原有的元素进行扁平化: + +```java +String[] strings = {"hello", "world"}; + +Arrays.stream(strings) + .map(x -> x.split("")) // 拆分得到: ['h','e','l','l','o'],['w','o','r','l','d'] + .flatMap(x -> Arrays.stream(x)) // 进行扁平化处理得到:'h','e','l','l','o','w','o','r','l','d' + .forEach(System.out::println); +``` + +上表的 `reduce()` 方法接收两个参数:第一个参数表示执行归约操作的初始值;第二个参数是上文我们介绍过的函数式接口 `BinaryOperator` ,使用示例如下: + +```java +Stream.iterate(0, x -> x + 1).limit(10) + .reduce(0, (a, b) -> a + b); //进行求和操作 +``` + +### 4.2 数值流 + +上面的代码等效于对 Stream 中的所有元素执行了求和操作,因此我们还可以调用简便方法 `sum()` 来进行实现,但是需要注意的是上面 `Stream.iterate()` 生成流中的元素类型都是包装类型: + +```java +Stream stream = Stream.iterate(0, x -> x + 1); //包装类型Integer +``` + +而 `sum()` 方法则是定义在 IntStream 上,此时需要将流转换为具体的数值流,对应的方法是 `mapToInt()`: + +````java +Stream.iterate(0, x -> x + 1).limit(10).mapToInt(x -> x).sum(); +```` + +类似的方法还有 `mapToLong()` 和 `mapToDouble()` 。如果你想要将数值流转换为原有的流,相当于对其中的元素进行装箱操作,此时可以调用 `boxed()` 方法: + +```java +IntStream intStream = Stream.iterate(0, x -> x + 1).limit(10).mapToInt(x -> x); +Stream boxed = intStream.boxed(); +``` + + + +## 五、收集器 + +Stream 中最强大一个终止操作是 `collect()` ,它接收一个收集器 Collector 作为参数,可以将流中的元素收集到集合中,或进行分组、分区等操作。Java 中内置了多种收集器的实现,可以通过 Collectors 类的静态方法进行调用,常用的如下: + +| 工厂方法 | 返回类型 | 用于 | +| ----------------- | --------------------- | ------------------------------------------------------------ | +| toList | List\ | 把流中所有元素收集到 List 中 | +| toSet | Set\ | 把流中所有元素收集到 Set 中 | +| toCollection | Collection\ | 把流中所有元素收集到指定的集合中 | +| counting | Long | 计算流中所有元素的个数 | +| summingInt | Integer | 将流中所有元素转换为整数,并计算其总和 | +| averagingInt | Double | 将流中所有元素转换为整数,并计算其平均值 | +| summarizingInt | IntSummaryStatistics | 将流中所有元素转换为整数,并返回值统计值,包含最大值、最小值、
总和与平均值等信息 | +| joining | String | 将流中所有元素转换为字符串,并使用给定连接符进行连接 | +| maxBy | Optional\ | 查找流中最大元素的 Optional | +| minBy | Optional\ | 查找流中最小元素的 Optional | +| reducing | 规约操作产生的类型 | 对流中所有元素执行归约操作 | +| collectingAndThen | 转换返回的类型 | 把流中所有元素收集到指定的集合中,再对集合执行特定转换操作 | +| groupingBy | Map> | 对流中所有元素执行分组操作 | +| partitionBy | Map> | 对流中所有元素执行分区操作 | + +使用示例如下: + +```java +Stream stream = Stream.of(1, 2, 3, 4, 4, 5, 6); + +stream.collect(Collectors.toSet()); // [1, 2, 3, 4, 5, 6] +stream.collect(Collectors.toList()); // [1, 2, 3, 4, 4, 5, 6] +stream.collect(Collectors.toCollection(ArrayList::new)); // [1, 2, 3, 4, 4, 5, 6] +stream.collect(Collectors.counting()); // 7 等效于 stream.count(); +stream.collect(Collectors.summarizingInt(x -> x)); // IntSummaryStatistics{count=7, sum=25, min=1, average=3.571429, max=6} +stream.collect(Collectors.maxBy((Integer::compareTo))); // Optional[6] +stream.collect(Collectors.reducing(1, (a, b) -> a * b)); // 等效于 stream.reduce(1, (a, b) -> a * b); +collect(Collectors.collectingAndThen(Collectors.toSet(), Set::size)); // 先把所有元素收集到Set中,再计算Set的大小 +``` + +> 注意:以上每个终止操作只能单独演示,因为对一个流只能执行一次终止操作。并且执行完终止操作后,就不能再对这个流执行任何操作,否则将抛出 `java.lang.IllegalStateException: stream has already been operated upon or closed` 异常。 + +### 5.2 分组 + +分组收集器可以实现类似数据库 groupBy 子句的功能。假设存在如下员工信息: + +```java +Stream stream = Stream.of(new Employee("张某", "男", "A公司", 20), + new Employee("李某", "女", "A公司", 30), + new Employee("王某", "男", "B公司", 40), + new Employee("田某", "女", "B公司", 50)); +``` + +```java +public class Employee { + + private String name; + private String gender; + private String company; + private int age; + + @Override + public String toString() {return "Employee{" + "name='" + name + '\'' + '}'; + } +} +``` + +此时如果需要按照公司进行分组,则可以使用 `groupingBy()` 收集器: + +```java +stream.collect(Collectors.groupingBy(Employee::getCompany)); + +对应的分组结果如下: +{ + B公司=[Employee{name='王某'}, Employee{name='田某'}], + A公司=[Employee{name='张某'}, Employee{name='李某'}] +} +``` + +如果想要计算分组后每家公司的人数,还可以为 `groupingBy()` 传递一个收集器 Collector 作为其第二个参数,调用其重载方法: + +```java +stream.collect(Collectors.groupingBy(Employee::getCompany, Collectors.counting())); + +对应的结果如下: +{ + B公司=2, + A公司=2 +} +``` + +因为第二个参数是一个 Collector,这意味着你可以再传入一个分组收集器来完成多级分组,示例如下: + +```java +stream.collect(Collectors.groupingBy(Employee::getCompany, Collectors.groupingBy(Employee::getGender))); + +对应的分组结果如下: +{ + B公司={女=[Employee{name='田某'}], 男=[Employee{name='王某'}]}, + A公司={女=[Employee{name='李某'}], 男=[Employee{name='张某'}]} +} +``` + +除此之外,也可以通过代码块来自定义分组条件,示例如下: + +```java +Map> collect = stream.collect(Collectors.groupingBy(employee -> { + if (employee.getAge() <= 30) { + return "青年员工"; + } else if (employee.getAge() < 50) { + return "中年员工"; + } else { + return "老年员工"; + } +})); + +对应的分组结果如下: +{ + 中年员工=[Employee{name='王某'}], + 青年员工=[Employee{name='张某'}, Employee{name='李某'}], + 老年员工=[Employee{name='田某'}] +} +``` + +### 5.3 分区 + +分区是分组的一种特殊情况,即将满足指定条件的分为一组,将不满足指定条件的分为另外一组,两者在使用上基本类似,示例如下: + +```java +stream.collect(Collectors.partitioningBy(x -> "A公司".equals(x.getCompany()))); + +对应的分区结果如下: +{ + false=[Employee{name='王某'}, Employee{name='田某'}], + true=[Employee{name='张某'}, Employee{name='李某'}] +} +``` + + + +## 六、并行流 + +想要将普通流转换为并行流非常简单,只需要调用 Stream 的 `parallel()` 方法即可: + +```java +stream.parallel(); +``` + +此时流中的所有元素会被均匀的分配到多个线程上进行处理。并行流内部使用的是 ForkJoinPool 线程池,它默认的线程数量就是处理器数量,可以通过 `Runtime.getRuntime().availableProcessors()` 来查看该值,通常不需要更改。 + +同时当前也无法为某个具体的流指定线程数量,只能通过修改系统属性 `java.util.concurrent.ForkJoinPool.common.parallelism` 的值来改变线程池大小,进而改变所有并行流的线程大小,示例如下: + +```java +System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","12"); +``` + +如果想将并行流改回普通的串行流,则只需要调用 Stream 的 `sequential()` 方法即可: + +```she +stream.sequential(); +``` + + + + + +## 参考文档 + + 厄马(Raoul-Gabriel Urma) / 弗斯科(Mario Fusco) / 米克罗夫特(Alan Mycroft) .**《Java 8实战》**. 人民邮电出版社 . 2016-04-01 \ No newline at end of file diff --git a/notes/Redis_分布式锁原理.md b/notes/Redis_分布式锁原理.md new file mode 100644 index 0000000..03130a3 --- /dev/null +++ b/notes/Redis_分布式锁原理.md @@ -0,0 +1,322 @@ +# Redis 分布式锁 + +## 一、实现原理 + +### 1.1 基本原理 + +JDK 原生的锁可以让不同**线程**之间以互斥的方式来访问共享资源,但如果想要在不同**进程**之间以互斥的方式来访问共享资源,JDK 原生的锁就无能为力。此时可以使用 Redis 或 Zookeeper 来实现分布式锁。 + +Redis 实现分布式锁的核心命令如下: + +```shell +SETNX key value +``` + +SETNX 命令的作用是如果指定的 key 不存在,则创建并将为其设置值,此时返回状态码为 1,否则为 0。如果状态码为 1,代表获得该锁;此时其他进程再次尝试创建时,都回返回 0 ,代表锁已经被占用。当获得锁的进程处理完成业务后,再通过 `del` 命令将该 key 删除,其他进程就可以再次竞争性地进行创建,获得该锁。 + +通常为了避免死锁,我们会为锁设置一个超时时间,在 Redis 中可以通过 `expire` 命令来进行实现: + +```shell +EXPIRE key seconds +``` + +这里我们将两者结合起来,并使用 Jedis 客户端来进行实现,其代码如下: + +```java +Long result = jedis.setnx("lockKey", "lockValue"); +if (result == 1) { + // 如果此处程序被异常终止(如直接kill -9进程),则设置超时的操作就无法进行,该锁就会出现死锁 + jedis.expire("lockKey", 3); +} +``` + +上面的代码存在原子性问题,即 setnx + expire 操作是非原子性的,如果在设置超时时间前,程序被异常终止,则程序就会出现死锁。此时可以将 SETNX 和 EXPIRE 两个命令写在同一个 Lua 脚本中,然后通过调用 Jedis 的 `eval()` 方法来执行,并由 Redis 来保证整个 Lua 脚本操作的原子性。这种方式实现比较繁琐,因此官方文档中推荐了另外一种更加优雅的实现方法: + +### 1.2 官方推荐 + +[官方文档]( Distributed locks with Redis) 中推荐直接使用 set 命令来进行实现: + +```shell +SET key value [EX seconds|PX milliseconds] [NX|XX] [KEEPTTL] +``` + +这里我们主要关注以下四个参数: + +- **EX** :设置超时时间,单位是秒; +- **PX** :设置超时时间,单位是毫秒; +- **NX** :当且仅当对应的 Key 不存在时候才进行设置; +- **XX**:当且仅当对应的 Key 不存在时候才进行设置。 + +这四个参数从 Redis 2.6.12 版本开始支持,因为当前大多数在用的 Redis 都已经高于这个版本,所以推荐直接使用该命令来实现分布式锁。对应的 Jedis 代码如下: + +```java +jedis.set("lockKey", "lockValue", SetParams.setParams().nx().ex(3)); +``` + +此时一条命令就可以完成值和超时时间的设置,并且因为只有一条命令,因此其原子性也得到了保证。但因为引入了超时时间来避免死锁,同时也存在了两个其他问题: + +![redis_分布式锁原理](../pictures/redis_分布式锁原理.png) + ++ **问题一**:当业务处理的时间超过过期时间后,由于锁已经被释放,此时其他进程就可以获得该锁,这意味着同时有两个进程进入了临界区,此时分布式锁就失效了; ++ **问题二**:如上图所示,当进程 A 业务处理完成后,此时删除的是进程 B 的锁,进而导致分布式锁又一次失效,进程 B 和 进程 C 同时进入了临界区。 + +针对问题二,我们可以在创建锁时为其指定一个唯一的标识作为 Key 的 Value,这里假设我们采用 `UUID + 线程ID` 来作为唯一标识: + +```java +String identifier = UUID.randomUUID() + ":" + Thread.currentThread().getId(); +jedis.set("LockKey", identifier, SetParams.setParams().nx().ex(3)); +``` + +然后在删除锁前,先将该唯一标识与锁的 Value 值进行比较,如果不相等,证明该锁不属于当前的操作对象,此时不执行删除操作。为保证判断操作和删除操作整体的原子性,这里需要使用 Lua 脚本来执行: + +```shell +if redis.call("get",KEYS[1]) == ARGV[1] then + return redis.call("del",KEYS[1]) +else + return 0 +end +``` + +这段脚本的意思是如果 value 的值与给定的值相同,则执行删除命令,否则直接返回状态码 0 。对应使用 Jedis 实现的代码如下: + +```java +String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; +jedis.eval(script, + Collections.singletonList("LockKey"), // keys的集合 + Collections.singletonList(identifier) // args的集合 + ); +``` + +接着再看问题一,问题一最简单的解决方法是:你可以估计业务的最大处理时间,然后保证设置的过期时间大于最大处理时间。但是由于业务需要面临各种复杂的情况,因此可能无法保证业务每一次都能在规定的过期时间内处理完成,此时可以使用延长锁时效的策略。 + +### 1.3 延长锁时效 + +延长锁时效的方案如下:假设锁超时时间是 30 秒,此时程序需要每隔一段时间去扫描一下该锁是否还存在,扫描时间需要小于超时时间,通常可以设置为超时时间的 1/3,在这里也就是 10 秒扫描一次。如果锁还存在,则重置其超时时间恢复到 30 秒。通过这种方案,只要业务还没有处理完成,锁就会一直有效;而当业务一旦处理完成,程序也会马上删除该锁。 + +Redis 的 Java 客户端 Redisson 提供的分布式锁就支持延长锁时效的机制,称为 WatchDog,直译过来就是 “看门狗” 机制。 + +以上讨论的都是单机环境下的 Redis 分布式锁,而想要保证 Redis 分布式锁是高可用,首先 Redis 得是高可用的,Redis 的高可用模式主要有两种:哨兵模式和集群模式。 + + + +## 二、哨兵模式与分布式锁 + +哨兵模式是主从模式的升级版,能够在故障发生时自动进行故障切换,选举出新的主节点。但由于 Redis 的复制机制是异步的,因此在哨兵模式下实现的分布式锁是不可靠的,原因如下: + ++ 由于主从之间的复制操作是异步的,当主节点上创建好锁后,此时从节点上的锁可能尚未创建。而如果此时主节点发生了宕机,从节点上将不会创建该分布式锁; ++ 从节点晋升为主节点后,其他进程(或线程)仍然可以在该新主节点创建分布式锁,此时就存在多个进程(或线程)同时进入了临界区,分布式锁就失效了。 + +因此在哨兵模式下,无法避免锁失效的问题。因此想要实现高可用的分布式锁,可以采取 Redis 的另一个高可用方案 —— Redis 集群模式。 + + + +## 三、集群模式与分布式锁 + +### 3.1 RedLock 方案 + +想要在集群模式下实现分布式锁,Redis 提供了一种称为 RedLock 的方案,假设我们有 N 个 Redis 实例,此时客户端的执行过程如下: + ++ 以毫秒为单位记录当前的时间,作为开始时间; ++ 接着采用和单机版相同的方式,依次尝试在每个实例上创建锁。为了避免客户端长时间与某个故障的 Redis 节点通讯而导致阻塞,这里采用快速轮询的方式:假设创建锁时设置的超时时间为 10 秒,则访问每个 Redis 实例的超时时间可能在 5 到 50 毫秒之间,如果在这个时间内还没有建立通信,则尝试下一个实例; ++ 如果在至少 N/2+1 个实例上都成功创建了锁。并且 `当前时间 - 开始时间 < 锁的超时时间` ,则认为已经获取了锁,锁的有效时间等于 `超时时间 - 花费时间`(如果考虑到不同 Redis 实例所在的服务器存在时钟漂移,则还需要减去时钟漂移); ++ 如果少于 N/2+1 个实例,则认为创建分布式锁失败,此时需要删除这些实例上已创建的锁,以便其他客户端进行创建。 ++ 该客户端在失败后,可以等待一个随机的时间后,再次进行重试。 + +以上就是 RedLock 的实现方案,可以看到主要是由客户端来实现的,并不真正涉及到 Redis 集群相关的功能。因此这里的 N 个 Redis 实例并不要求是一个真正的 Redis 集群,它们彼此之间可以是完全独立的,但由于只需要半数节点获得锁就能真正获得锁,因此对于分布式锁功能而言,其仍然是高可用的。后面使用 Redisson 来演示 RedLock 时会再次验证这一点。 + + + +### 3.2 低延迟通讯和多路复用 + +实现 RedLock 方案的客户端与所有 Redis 实例进行通讯时,必须要保证低延迟,而且最好能使用多路复用技术来保证一次性将 SET 命令发送到所有 Redis 节点上,并获取到对应的执行结果。假设网络延迟高,此时客户端 A 和 B 分别尝试创建锁: + +```shell +SET key 随机数A EX 3 NX #A客户端 +SET key 随机数B EX 3 NX #B客户端 +``` + +假设客户端 A 在一半节点上创建了锁,而客户端 B 在另外一半节点上创建了锁,此时两个客户端都无法获取到锁。如果并发很高,则可能存在多个客户端分别在部分节点上创建了锁,而没有一个客户端的数量超过 N/2+1。这也就是上面过程的最后一步中,强调一旦客户端失败后,需要等待一个随机时间后再进行重试的原因,如果是一个固定时间,则所有失败的客户端又同时发起重试,情况就还是一样。 + +因此最佳的实现就是客户端的 SET 命令能几乎同时到达所有节点,并几乎同时接受到所有执行结果。 想要保证这一点,低延迟的网络通信极为关键,下文介绍的 Redisson 就采用 Netty 来实现了这一功能。 + + + +### 3.3 持久化与高可用 + +为了保证高可用,所有 Redis 节点都需要开启持久化。假设不开启持久化,假设进程 A 获得锁后正在处理业务逻辑,此时节点宕机重启,因为锁数据丢失了,其他进程便可以再次获得该锁,因此所有 Redis 节点都需要开启 AOF 持久化方式。 + +AOF 默认的同步机制为 `everysec`,即每秒进程一次持久化,此时能够兼顾性能与数据安全,发生意外宕机的时,最多会丢失一秒的数据。但如果碰巧就是在这一秒的时间内进程 A 创建了锁,此时其他进程也可以获得该锁,锁的互斥性也就失效了。要解决这个问题有两种方式: + ++ **方式一**:修改 Redis.conf 中 `appendfsync` 的值为 always,即每次命令后都进行持久化,此时降低了 Redis 性能,进而也会降低了分布式锁的性能,但锁的互斥性得到了绝对的保证; ++ **方式二**:一旦节点宕机了,等到锁的超时时间过了之后才进行重启,此时相当于原有锁自然失效(你需要保证业务在自己设定的超时时间内能完成),这种方案称为延时重启。 + + + +## 四、Redisson + +Redisson 是 Redis 的 Java 客户端,它提供了各种的 Redis 分布式锁的实现,如可重入锁、公平锁、RedLock、读写锁等等,并且在实现上考虑得也更加全面,适用于生产环境下使用。 + +### 4.1 分布式锁 + +使用 Redisson 来创建单机版本分布式锁非常简单,示例如下: + +```java +// 1.创建RedissonClient,如果与spring集成,可以将RedissonClient声明为Bean,在使用时注入即可 +Config config = new Config(); +config.useSingleServer().setAddress("redis://192.168.0.100:6379"); +RedissonClient redissonClient = Redisson.create(config); + +// 2.创建锁实例 +RLock lock = redissonClient.getLock("myLock"); +try { + //3.尝试获取分布式锁,第一个参数为等待时间,第二个参数为锁过期时间 + boolean isLock = lock.tryLock(10, 30, TimeUnit.SECONDS); + if (isLock) { + // 4.模拟业务处理 + System.out.println("处理业务逻辑"); + Thread.sleep(20 * 1000); + } +} catch (Exception e) { + e.printStackTrace(); +} finally { + //5.释放锁 + lock.unlock(); +} +redissonClient.shutdown(); +``` + +此时对应在 Redis 中的数据结构如下: + +![redis_分布式锁_cli1](../pictures/redis_分布式锁_cli1.png) + +可以看到 key 就是代码中设置的锁名,而 value 值的类型是 hash,其中键 `9280e909-c86b-43ec-b11d-6e5a7745e2e9:13` 的格式为 `UUID + 线程ID`,键对应的值为 1,代表加锁的次数。之所以要采用 hash 这种格式,主要是因为 Redisson 创建的锁是具有重入性的,即你可以多次进行加锁: + +```java +boolean isLock1 = lock.tryLock(0, 30, TimeUnit.SECONDS); +boolean isLock2 = lock.tryLock(0, 30, TimeUnit.SECONDS); +``` + +此时对应的值就会变成 2,代表加了两次锁: + +![redis_分布式锁_cli2](../pictures/redis_分布式锁_cli2.png) + +当然和其他重入锁一样,需要保证加锁的次数和解锁的次数一样,才能完全解锁: + +```java +lock.unlock(); +lock.unlock(); +``` + + + +### 4.2 RedLock + +Redisson 也实现了 Redis 官方推荐的 RedLock 方案,这里我们启动三个 Redis 实例进行演示,它们彼此之间可以是完全独立的,并不需要进行集群的相关配置: + +```shell +$ ./redis-server ../redis.conf +$ ./redis-server ../redis.conf --port 6380 +$ ./redis-server ../redis.conf --port 6381 +``` + +对应的代码示例如下: + +```java +// 1.创建RedissonClient +Config config01 = new Config(); +config01.useSingleServer().setAddress("redis://192.168.0.100:6379"); +RedissonClient redissonClient01 = Redisson.create(config01); +Config config02 = new Config(); +config02.useSingleServer().setAddress("redis://192.168.0.100:6380"); +RedissonClient redissonClient02 = Redisson.create(config02); +Config config03 = new Config(); +config03.useSingleServer().setAddress("redis://192.168.0.100:6381"); +RedissonClient redissonClient03 = Redisson.create(config03); + +// 2.创建锁实例 +String lockName = "myLock"; +RLock lock01 = redissonClient01.getLock(lockName); +RLock lock02 = redissonClient02.getLock(lockName); +RLock lock03 = redissonClient03.getLock(lockName); + +// 3. 创建 RedissonRedLock +RedissonRedLock redLock = new RedissonRedLock(lock01, lock02, lock03); + +try { + boolean isLock = redLock.tryLock(10, 300, TimeUnit.SECONDS); + if (isLock) { + // 4.模拟业务处理 + System.out.println("处理业务逻辑"); + Thread.sleep(200 * 1000); + } +} catch (Exception e) { + e.printStackTrace(); +} finally { + //5.释放锁 + redLock.unlock(); +} + +redissonClient01.shutdown(); +redissonClient02.shutdown(); +redissonClient03.shutdown(); +``` + +此时每个 Redis 实例上锁的情况如下: + +![redis_分布式锁_cli3](../pictures/redis_分布式锁_cli3.png) + +可以看到每个实例上都获得了锁。 + +### 4.3 延长锁时效 + +最后,介绍一下 Redisson 的 WatchDog 机制,它可以用来延长锁时效,示例如下: + +```java +Config config = new Config(); +// 1.设置WatchdogTimeout +config.setLockWatchdogTimeout(30 * 1000); +config.useSingleServer().setAddress("redis://192.168.0.100:6379"); +RedissonClient redissonClient = Redisson.create(config); + +// 2.创建锁实例 +RLock lock = redissonClient.getLock("myLock"); +try { + //3.尝试获取分布式锁,第一个参数为等待时间 + boolean isLock = lock.tryLock(0, TimeUnit.SECONDS); + if (isLock) { + // 4.模拟业务处理 + System.out.println("处理业务逻辑"); + Thread.sleep(60 * 1000); + System.out.println("锁剩余的生存时间:" + lock.remainTimeToLive()); + } +} catch (Exception e) { + e.printStackTrace(); +} finally { + //5.释放锁 + lock.unlock(); +} +redissonClient.shutdown(); +``` + +这里我们通过 `config.setLockWatchdogTimeout(30 * 1000)` 将 lockWatchdogTimeout 的值设置为 30000 毫秒(默认值也是 30000 毫秒)。lockWatchdogTimeout 只会对那些没有设置锁超时时间的锁生效,所以我们这里调用的是两个参数的 `tryLock()` 方法: + +```java +boolean tryLock(long time, TimeUnit unit) throws InterruptedException; +``` + +而不是包含超时时间的三个参数的 `tryLock()` 方法: + +```java +boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException; +``` + +Redisson 的 WatchDog 机制会以 lockWatchdogTimeout 的 1/3 时长为周期(在这里就是 10 秒)对所有未设置超时时间的锁进行检查,如果业务尚未处理完成(也就是锁还没有被程序主动删除),Redisson 就会将锁的超时时间重置为 lockWatchdogTimeout 指定的值(在这里就是设置的 30 秒),直到锁被程序主动删除。因此在上面的例子中可以看到,不论将模拟业务的睡眠时间设置为多长,其锁都会存在一定的剩余生存时间,直至业务处理完成。 + +反之,如果明确的指定了锁的超时时间 leaseTime,则以 leaseTime 的时间为准,WatchDog 机制对明确指定超时时间的锁不会生效。 + + + +## 参考资料 + ++ [Distributed locks with Redis](https://redis.io/topics/distlock) ++ [Redisson Distributed locks and synchronizers](https://github.com/redisson/redisson/wiki/8.-distributed-locks-and-synchronizers) \ No newline at end of file diff --git a/notes/Redis_持久化.md b/notes/Redis_持久化.md index dd25dcb..da6eec2 100644 --- a/notes/Redis_持久化.md +++ b/notes/Redis_持久化.md @@ -34,10 +34,10 @@ RDB 机制是以指定的时间间隔将 Redis 中的数据生成快照并保存 除了手动使用命令触发外,在某些场景下也会自动触发 Redis 的 RDB 机制: -+ 在 `redis.conf` 中配置了 `save m n` ,表示如果在 m 秒内存在了 n 次修改操作时,则自动触发`bgsave`; -+ 如果从节点执行全量复制操作,则主节点自动执行`bgsave`,并将生成的 RDB 文件发送给从节点; -+ 执行 `debug reload` 命令重新加载 Redis 时,会触发`save`操作; -+ 执行 `shutdown` 命令时候,如果没有启用 AOF 持久化则默认采用`bgsave`进行持久化。 ++ 在 `redis.conf` 中配置了 `save m n` ,表示如果在 m 秒内存在了 n 次修改操作时,则自动触发 `bgsave`; ++ 如果从节点执行全量复制操作,则主节点自动执行 `bgsave`,并将生成的 RDB 文件发送给从节点; ++ 执行 `debug reload` 命令重新加载 Redis 时,会触发 `save` 操作; ++ 执行 `shutdown` 命令时候,如果没有启用 AOF 持久化则默认采用 `bgsave ` 进行持久化。 ### 2.3 相关配置 @@ -45,7 +45,7 @@ RDB 机制是以指定的时间间隔将 Redis 中的数据生成快照并保存 RDB 文件默认保存在 Redis 的工作目录下,默认文件名为 `dump.rdb`,可以通过静态或动态方式修改: -+ 静态配置:通过修改 `redis.conf` 中的工作目录`dir`和数据库存储文件名`dbfilename`两个配置; ++ 静态配置:通过修改 `redis.conf` 中的工作目录 `dir` 和数据库存储文件名 `dbfilename` 两个配置; + 动态修改:通过在命令行中执行以下命令: @@ -72,7 +72,7 @@ AOF 是 Redis 提供的另外一种持久化的方式,它以独立日志的方 ### 3.2 同步策略 -Redis 提供了三种同步策略,用于控制 AOF 缓冲区同步数据到磁盘上的行为,由参数`appendfsync`控制: +Redis 提供了三种同步策略,用于控制 AOF 缓冲区同步数据到磁盘上的行为,由参数 `appendfsync` 控制: | 可选配置 | 说明 | | -------- | ------------------------------------------------------------ | @@ -85,11 +85,11 @@ write 和 fsync 操作说明: - write 操作会触发延迟写机制,Linux 在内核提供页缓冲区用来提高硬盘的 IO 性能,write 操作在写入系统缓冲区后直接返回。同步操作依赖于系统调度机制,例如缓冲区页空间写满或达到特定时间周期。 同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。 - fsync 针对单个文件操作,做强制硬盘同步,fsync 操作将阻塞直到写入硬盘完成后返回,它保证了数据持久化的安全。 -Redis 默认的同步机制为`everysec`,此时能够兼顾性能和保证数据安全,在发生意外宕机的时,最多会丢失一秒的数据。 +Redis 默认的同步机制为 `everysec`,此时能够兼顾性能和保证数据安全,在发生意外宕机的时,最多会丢失一秒的数据。 ### 3.3 相关配置 -想要使用 AOF 功能,需要配置 `appendonly `的值为`yes`,默认值为`no`。默认 AOF 的文件名为 `appendonly.aof`, 可以通过修改`appendfilename`的值进行修改,和 RDB 文件的保存位置一样,默认保存在 Redis 的工作目录下。 +想要使用 AOF 功能,需要配置 `appendonly ` 的值为 `yes`,默认值为 `no`。默认 AOF 的文件名为 `appendonly.aof`, 可以通过修改`appendfilename` 的值进行修改,和 RDB 文件的保存位置一样,默认保存在 Redis 的工作目录下。 ## 四、对比分析 diff --git a/notes/ZooKeeper_分布式锁原理.md b/notes/ZooKeeper_分布式锁原理.md new file mode 100644 index 0000000..87a1c8c --- /dev/null +++ b/notes/ZooKeeper_分布式锁原理.md @@ -0,0 +1,357 @@ +# ZooKeeper 分布式锁原理 + +## 一、实现原理 + +JDK 原生的锁可以让不同**线程**之间以互斥的方式来访问共享资源,但如果想要在不同**进程**之间以互斥的方式来访问共享资源,JDK 原生的锁就无能为力。此时可以使用 Redis 或 Zookeeper 来实现分布式锁。 + +### 1.1 临时节点方案 + +![zookeeper_分布式锁_临时节点方法](../pictures/zookeeper_分布式锁_临时节点方法.png) + +临时节点方案的原理如下: + ++ 让多个进程(或线程)竞争性地去创建同一个临时节点,由于 ZooKeeper 不允许存在两个完全相同节点,因此必然只有一个进程能够抢先够创建成功 ; ++ 假设进程 A 成功创建,则它获得了该分布式锁。此时其他进程需要在 parent_node 上注册监听,监听其下所有子节点的变化,并挂起当前线程; ++ 当 parent_node 下有子节点发生变化时候,它会通知所有在其上注册了监听的进程。这些进程需要判断是否是对应的锁节点上的删除事件。如果是,则让挂起的线程继续执行,并尝试再次获取锁。 + +这里之所以使用临时节点是为了避免死锁:进程 A 正常执行完业务逻辑后,会主动地去删除该节点,释放锁。但如果进程 A 意外宕机了,由于声明的是临时节点,因此该节点也会被移除,从而避免死锁。 + +临时节点方案的实现比较简单,但是其缺点也比较明显: + ++ **缺点一**:当 parent_node 下其他锁变动或者被删除时,进程 B,C,D 也会收到通知,但是显然它们并不关心其他锁的释放情况。如果 parent_node 下存在大量的锁,并且程序处于高并发状态下,则 ZooKeeper 集群就需要频繁地通知客户端进程,这会带来大量的网络开销; ++ **缺点二**:采用临时节点方案创建的锁是非公平的,也就是说在进程 A 释放锁后,进程 B,C,D 发起重试的顺序与其收到通知的时间有关,而与其第一次尝试获取锁的时间无关,即与等待时间的长短无关。 + +### 1.2 临时有序节点方案 + +![zookeeper_分布式锁_临时有序节点方案](../pictures/zookeeper_分布式锁_临时有序节点方案.png) + +采用临时有序节点时,对应的流程如下: + ++ 每个进程(或线程)都会尝试在 parent_node 下创建临时有序节点,根据临时有序节点的特性,所有的进程都会创建成功; ++ 然后每个进程需要获取 parent_node 下该锁的所有临时节点的信息,并判断自己是否是最小的一个节点,如果是,则代表获得该锁; ++ 如果不是,则挂起当前线程。并对其前一个节点注册监听(这里可以通过 exists 方法传入需要触发 Watch 事件); ++ 如上图所示,当进程 A 处理完成后,会触发进程 B 注册的 Watch 事件,此时进程 B 就知道自己获得了锁,从而可以将挂起的线程继续,开始业务的处理。 + +这里需要注意的是:如果进程 B 创建了临时节点,并且通过比较后知道自己不是最小的一个节点,但还没有注册监听;而此时 A 进程恰好处理完成并删除了 01 节点,此时调用 exist 方法时会抛出 IllegalArgumentException 异常。这虽然是一个异常,但是却代表进程 B 获得了锁,因此进程 B 可以开始执行业务逻辑。 + +临时有序节点方案正好解决了临时节点方案的两个缺点: + ++ 每个临时有序节点只需要关心它的上一个节点,而不需要关心其他的额外节点和额外事件; ++ 实现的锁是公平的,先到达的进程创建的临时有序节点的值越小,能更快地获得锁。 + +临时有序节点方案的另外一个优点是其能够实现共享锁,比如读写锁中的读锁。 + +### 1.3 读写锁 + +如下图所示,可以将临时有序节点分为读锁节点和写锁节点: + ++ 对于读锁节点而言,其只需要关心前一个写锁节点的释放。如果前一个写锁释放了,则多个读锁节点对应的线程可以并发地读取数据; ++ 对于写锁节点而言,其只需要关心前一个节点的释放,而不需要关心前一个节点是写锁节点还是读锁节点。因为为了保证有序性,写操作必须要等待前面的读操作或者写操作执行完成。 + +![zookeeper_分布式读写锁](../pictures/zookeeper_分布式读写锁.png) + + + +## 二、 Apache Curator + +### 2.1 基本使用 + + Apache Curator 是 ZooKeeper 的 Java 客户端,它基于临时有序节点方案实现了分布式锁、分布式读写锁等功能。基本使用如下: + +```java +RetryPolicy retryPolicy = new RetryNTimes(3, 5000); +CuratorFramework client = CuratorFrameworkFactory.builder() + .connectString("192.168.0.105:2181") + .sessionTimeoutMs(10000).retryPolicy(retryPolicy) + .namespace("mySpace").build(); +client.start(); + +// 1. 创建分布式锁 +InterProcessMutex lock = new InterProcessMutex(client, "/distributed/myLock"); +// 2.尝试获取分布式锁 +if (lock.acquire(10, TimeUnit.SECONDS)) { + try { + System.out.println("模拟业务耗时"); + Thread.sleep(3 * 1000); + } finally { + // 3.释放锁 + lock.release(); + } +} +client.close(); +``` + +这里需要事先导入 Apache Curator 和 ZooKeeper 相关的依赖,并保证 ZooKeeper 版本与服务器上 ZooKeeper 的版本一致: + +```xml + + org.apache.curator + curator-framework + 4.3.0 + + + org.apache.curator + curator-recipes + 4.3.0 + + + org.apache.zookeeper + zookeeper + 3.4.14 + +``` + +之后就可以启动多个程序进程来进程测试,ZooKeeper 上的数据结构如下: + +![zookeeper_分布式锁_cli](../pictures/zookeeper_分布式锁_cli.png) + +在我们指定的路径下,会依次创建多个临时有序节点,而当业务逻辑处理完成后,这些节点就会被移除。这里我们使用的是单机版本的 ZooKeeper ,而集群环境下也是一样,和 Redis 主从模式下的延迟复制会导致数据不一致的情况不同,ZooKeeper 各个节点上的数据一致性可以由其自身来进行保证。 + + + +### 2.2 源码解析 + +Apache Curator 底层采用的是临时有序节点的实现方案,下面我们来看一下其源码中具体是如何实现的: + +#### 1. 获取锁源码解析 + +上面最核心的获取锁的方法 `acquire()` ,其定义如下: + +```java +@Override +public boolean acquire(long time, TimeUnit unit) throws Exception{ + return internalLock(time, unit); +} +``` + +它在内部调用了 `internalLock()` 方法: + +```java +// threadData是一个线程安全的Map,其中Thread是持有锁的线程,LockData是锁数据 +private final ConcurrentMap threadData = Maps.newConcurrentMap(); + +private boolean internalLock(long time, TimeUnit unit) throws Exception{ + Thread currentThread = Thread.currentThread(); + // 首先查看threadData中是否已经有当前线程对应的锁 + LockData lockData = threadData.get(currentThread); + if ( lockData != null ){ + //如果锁已存在,则将其计数器加1,这一步是为了实现可重入锁 + lockData.lockCount.incrementAndGet(); + return true; + } + // 【核心方法:尝试获取锁】 + String lockPath = internals.attemptLock(time, unit, getLockNodeBytes()); + // 如果获取到锁,则将其添加到threadData中 + if ( lockPath != null ){ + LockData newLockData = new LockData(currentThread, lockPath); + threadData.put(currentThread, newLockData); + return true; + } + return false; + } +``` + +这里面真正去尝试创建锁的方法是 `attemptLock()`: + +```java +String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception{ + final long startMillis = System.currentTimeMillis(); + final Long millisToWait = (unit != null) ? unit.toMillis(time) : null; + final byte[] localLockNodeBytes = (revocable.get() != null) ? new byte[0] : lockNodeBytes; + int retryCount = 0; // 重试次数 + String ourPath = null; + boolean hasTheLock = false; + boolean isDone = false; + + // 当出现NoNodeException异常时候依靠该循环进行重试 + while ( !isDone ){ + isDone = true; + try{ + // 【核心方法:根据锁路径来创建对应的节点】 + ourPath = driver.createsTheLock(client, path, localLockNodeBytes); + // 【核心方法:获取锁】 + hasTheLock = internalLockLoop(startMillis, millisToWait, ourPath); + } + catch ( KeeperException.NoNodeException e ){ + // 如果出现异常,并且还没有到达给ZooKeeper配置的最大重试时间或最大重试次数,则循环继续,并再次尝试获取锁 + if ( client.getZookeeperClient().getRetryPolicy() + .allowRetry(retryCount++,System.currentTimeMillis() - startMillis, + RetryLoop.getDefaultRetrySleeper()) ){ + isDone = false; + }else{ + throw e; + } + } + } + + // 如果获取到锁,则跳出循环,并返回锁的路径 + if ( hasTheLock ){ + return ourPath; + } + return null; + } +``` + +这里两个核心的方法是 `createsTheLock()` 和 `internalLockLoop()` 。createsTheLock 的实现比较简单,就是根据我们指定的路径来创建临时节点有序节点: + +```java +@Override +public String createsTheLock(CuratorFramework client, String path, byte[] lockNodeBytes) throws Exception{ + String ourPath; + // 如果lockNodeBytes不为空,则创建一个含数据的临时有序节点 + if ( lockNodeBytes != null ){ + ourPath = client.create().creatingParentContainersIfNeeded().withProtection(). + withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path, lockNodeBytes); + }else{ + //否则则创建一个空的临时有序节点 + ourPath = client.create().creatingParentContainersIfNeeded().withProtection(). + withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path); + } + // 返回创建好的节点路径 + return ourPath; +} +``` + +这里创建好的临时节点的路径会作为参数传递给 `internalLockLoop()` 方法。在文章开头介绍原理时候,我们说过每个线程创建好临时有序节点后,还需要判断它所创建的临时有序节点是否是当前最小的节点,`internalLockLoop()` 方法主要做的就是这事: + +```java +private boolean internalLockLoop ( long startMillis, Long millisToWait, String ourPath) throws Exception { + // 是否持有锁 + boolean haveTheLock = false; + boolean doDelete = false; + try { + if (revocable.get() != null) { + client.getData().usingWatcher(revocableWatcher).forPath(ourPath); + } + // 如果连接ZooKeeper客户端处于启动状态,也就是想要获取锁的进程仍然处于运行状态,并且还没有获取到锁,则循环继续 + while ((client.getState() == CuratorFrameworkState.STARTED) && !haveTheLock) { + // 对所当前所有的子节点按照从小到大进行排序 + List children = getSortedChildren(); + // 将createsTheLock方法获得的临时有序节点的路径进行截取,只保留节点名的部分 + String sequenceNodeName = ourPath.substring(basePath.length() + 1); + // 判断当前节点是否是最小的一个节点 + PredicateResults predicateResults = driver. + getsTheLock(client, children, sequenceNodeName, maxLeases); + // 如果当前节点就是最小的一个节点(排他锁情况),则此时就获得了锁 + if (predicateResults.getsTheLock()) { + haveTheLock = true; + } else { + // 如果当前节点不是最小的一个节点,先拼接并获取前一个节点完整的路径 + String previousSequencePath = basePath + "/" + predicateResults.getPathToWatch(); + synchronized (this) { + try { + // 然后对前一个节点进行监听 + client.getData().usingWatcher(watcher).forPath(previousSequencePath); + // 如果设置了等待时间 + if (millisToWait != null) { + // 将等待时间减去到目前为止所耗费的时间 + millisToWait -= (System.currentTimeMillis() - startMillis); + startMillis = System.currentTimeMillis(); + // 如果等待时间小于0,则说明我们耗费的时间已经超过了等待时间,此时获取的锁无效,需要删除它 + if (millisToWait <= 0) { + //设置删除标志位,并退出循环 + doDelete = true; + break; + } + // 如果还有剩余时间,则等待获取锁 + wait(millisToWait); + } else { + // 如果没有设置等待时间,则持续等待获取锁 + wait(); + } + } catch (KeeperException.NoNodeException e) { + // 这个异常抛出时,代表对前一个节点设置监听时,前一个节点已经不存在(被释放),此时捕获该异常, + // 但不需要进行任何额外操作,因为循环会继续,就可以再次尝试获取锁 + } + } + } + } + } catch (Exception e) { + ThreadUtils.checkInterrupted(e); + doDelete = true; + throw e; + } finally { + // 如果抛出了异常或者超时了,都删除掉上一个方法createsTheLock创建的临时有序节点,以便后面的进程进行锁的获取 + if (doDelete) { + deleteOurPath(ourPath); + } + } + return haveTheLock; +} +``` + +这里对上面判断当前节点是否是持有锁的节点的 getsTheLock 方法进行一下说明: + +```java +PredicateResults predicateResults = driver.getsTheLock(client, children, sequenceNodeName, maxLeases); +``` + +和上文介绍的一样,判断当前节点是否是持有锁的节点,在不同锁类型(如读写锁和互斥锁)的判断是不同的,因此 getsTheLock 方法有着不同的实现。这里以StandardLockInternalsDriver 为例,它使用的是互斥锁的判断规则:只要当前节点是最小的一个节点,就能持有锁: + +```java + public PredicateResults getsTheLock(CuratorFramework client, List children, + String sequenceNodeName, int maxLeases) throws Exception { + // 获取当前节点在已经排好序的节点中的下标index + int ourIndex = children.indexOf(sequenceNodeName); + // 如果ourIndexx小于0,则抛出NoNodeException的异常 + validateOurIndex(sequenceNodeName, ourIndex); + // 如果ourIndex小于maxLeases(默认值是1),则代表它就是0,也就是从小到大排好序的集合中的第一个,也就是最小的一个 + boolean getsTheLock = ourIndex < maxLeases; + // 如果是最小的一个,此时就已经获取到锁,不需要返回前一个节点的名称,否则需要返回前一个节点的名称,用于后续的监听操作 + String pathToWatch = getsTheLock ? null : children.get(ourIndex - maxLeases); + return new PredicateResults(pathToWatch, getsTheLock); + } +``` + +这里解释一下 maxLease 这个参数的意义:默认值为 1,就是互斥锁;如果默认值大于 1,假设 maxLease 的值是 5,则前 5 个临时有序节点都可以认为是能持有锁的节点,此时最多可以有 5 个线程并发访问临界区, 在功能上类似于 Java 中Semaphore(信号量)机制 。 + + + +#### 2. 释放锁源码解析 + +以上就是所有获取锁的源码解析,而释放锁的过程就比较简单了。`release()` 方法的源码如下: + +```java +public void release() throws Exception { + Thread currentThread = Thread.currentThread(); + // 根据当前线程来获取锁信息 + InterProcessMutex.LockData lockData = threadData.get(currentThread); + // 如果获取不到,则当前线程不是锁的持有者,此时抛出异常 + if (lockData == null) { + throw new IllegalMonitorStateException("You do not own the lock: " + basePath); + } + // 因为Zookeeper实现的锁具有重入性,所以将其计数器减少1 + int newLockCount = lockData.lockCount.decrementAndGet(); + if (newLockCount > 0) { + return; + } + // 如果计数器的值小于0,代表解锁次数大于加锁次数,此时抛出异常 + if (newLockCount < 0) { + throw new IllegalMonitorStateException("Lock count has gone negative for lock: " + basePath); + } + try { + // 如果到达这一步,则说明计数器的值正好等于0,此时可以真正将节点删除,释放锁 + internals.releaseLock(lockData.lockPath); + } finally { + // 将锁信息从threadData移除 + threadData.remove(currentThread); + } +} +``` + +真正删除锁节点的方法存在于 `releaseLock()` 中,其源码如下: + +```java +final void releaseLock(String lockPath) throws Exception{ + client.removeWatchers(); + revocable.set(null); + deleteOurPath(lockPath); //删除ZooKeeper上对应的节点 +} +``` + + + +## 参考资料 + ++ 倪超 . 从 Paxos 到 Zookeeper——分布式一致性原理与实践 . 电子工业出版社 . 2015-02-01 ++ https://curator.apache.org/curator-recipes/index.html \ No newline at end of file diff --git a/pictures/redis_分布式锁_cli1.png b/pictures/redis_分布式锁_cli1.png new file mode 100644 index 0000000000000000000000000000000000000000..aee5f790883caf3941bd478931b6a380b47f5c67 GIT binary patch literal 8029 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 literal 0 HcmV?d00001 diff --git a/pictures/redis_分布式锁_cli2.png b/pictures/redis_分布式锁_cli2.png new file mode 100644 index 0000000000000000000000000000000000000000..bb14b7554762e1e00ca2bc4cd2319804f514fb59 GIT binary patch literal 6007 zcmb7|RaBJWzoD#=P~ zdt~fpN?XtNV7vW~HxK`sRN3XCfB!@kr&vAw#`Jx^ODs_%B9vsXcPW_u?@MrUF1;wd zvB4W+O^=hfpU3SvTEs$XIU)BrUF&mIrTz4$J~bco&4&7lq!fCm-n@NaSO<;Y&5s!$ z+Kl%QxY-ajzuZhcylx4;_LG8uNV^7({Wo`kRd|3$C8{(^;+dlY2=V9bXjHqY5$V+^ zpO5OUfa^1oOf_pL;_A)Mts~{96Mz1V6nohh_+yf)8cmZ^Dng#yewiT*IDn2P_LZGv zRZY%nB9~JTK^h)OTB`o#s{0g!L-}WT0rcxO)iZWaN~+qzUnz;D$R$Wv1PM(D8$>D2H?oo{ z*nk%)x+L%l78aMlX}mor!HZwyVE6V>Ng$-6NF07}9Y@kcE+LUc-%>m2ePn$}*4?bX za6G?AGzYKu7yJ%qJxgA6np=i9JZqK`5l=vEb{12GyF6ozl#M!Fg2mTF4{rvYnu$%O4hxftRe_TYi79g9yJ5FjPtiDe0DN!QZ;IHJ7# z_Y||}zi=IZx3o0OqmIRe(Y>ofs+x~)SWFRz(z_Qu<3pK_u_o8&fAG34er=#w6{6uR zqxKx)duNmkjQV-(zzfA5%WDIq#<&o$1=TNq3$*E_$feSlHNTz?APEceUFx(eE!nFB z0GU_Lt*=cSKTVgX)apGM=In@`EEXV^kdkBatVG%M-CBfCO8yq^4!BF?Qauxm$PZk5 zd-K;yhfd;to8IChyP9Amtc$h3eBG3jYpU=dwLL&szg~t{Vn6t-clN!>PCCU;Y{h|~ zn^RZ#;YP{%y^J69@^^E;L)l?3V(FHZv{&{1MA5zz@(<@P z%Y%~|3S803oPKJz24mq*xv7zfrRLr|9V_i#pJefRcx`(Zs4YGf=UU{()7j!B}$LHgOSK3w)323oH;v^D=`+)a>k( z+pnM0xgmOg53;`D2&}Cw`Cv{&g@}&?S-WlPU_Btc$9K(}x%;3yXDyr^Jku`iXY!R} zlYOCNJ(a0!<~9;vXuAm2#RXAt@yyWJLoK}6^X1iwv-URPuad{jdV5V0p%m!h#Z=*z zuia>ifgp{GLv?W}>?!2nSzEKdA9fml*o_|mJka^H5jUFv5O$q$FD)~2$9CO~pLhbg z6x6&-tDhfL*e`EX6cwCM^dORDgA;LjYStOSKWJ55nS`$sTz=S}7<1GumPdlYqXro8 zuY{F@RdyL+r8kMRyxx>#0HmCDjFvO&A(#wZaa#m3W&4G9+O>Ka`f2bsgVySE>JHW{ z3&X&+?H4&oWzzT5$SF!I#%a}jH)aj7=i~U(f_kEABslFMfW7~Ltanf{wyW?%Tb zPRlW8m)~JaO~XE}5dk>w^(==ZJ{Hd+T(u&zf3364pvZ>qy@$*z) zFM3KRmJG`0B?fp^f@1Pz&wDvv)WZbkl4?HJOtOa7q6n;EBB(dZ_3{zTxvzOsw;tx{ zWAkC36b&AmrOtg8mNr%+i{W5EC>_SWUbZdaPocyWpi=BHE}vIZfB+4T(CrZ!TAAg^ zxL<0anveybQ@_LZ^Np*CRivDut6r1&;ER1Y(m^#o|r(DntfXZQGCF zeE#=$JZ4x;EV~GnBXfO%gnEGp=V(=f9S|(DO_!@q@{TfJOr@^?Dd!yDbV!aG@9sCDdjY* zpgz{UR=IJh#i7$QgFRxM=&{jKXYtP@PP@uTA^h(HOaw{Gth@{e_`2gl3ZzB(HWJ*K zQ=$Z}dp7i$?6*qXzb4p~Mnvr5=3zK;ClFi!0lgtLmn+#NqwQMI|78jRWe54y{IyNi z4o^URf0P0hzWJ44LLhJ{oN1UGqYh#n?ocNPKAhQ7;1_0=9lKwF?%cXUGG+Jk69l(| ztmc?W&+boy*|rs`jO=k;QBmHxs)v)_lByUyL5*b6Ml!`CnVj7e!J)VZ5s=*W<2PS* zXuMTPsv9fM^^ooj@o1uwkzzTR;fqszvKKK=zR`+yuXby2!CG61d=d=T-8)xy&J8Iq zX#fCnqX*iht8oCy%H@cb*zFb@bFt^_GIsUKl8O|C3E5wp|HaF%cSA^5l%-Io?ohQZ z*ih~Uop`BZ)asj~`t89&FQdrwBS$b65Kwgw>5ip4j60W#*oFMohZ7gzT>%Y2u=6NL z)F3o@1>3?ht1qL)?OZtdZf|fp$)lyR(0V?Wt;rY@C`{=gWScn$gaV*q z4sIwThPtHmq2_F;>zyj)yitw(26o!A6g&E{c*>MS`RW+~E>m|48KkkIE}Isk<1{F^ z{WG+ykD6x9W{S6v;C1;hs8HpnDP%4+veYUhq&Dm}EHQHVF^H6LvjX%hQ3?0;#0ENk z<3I=^ks643jZhzc*F)u=pUo$wCtl_=Wg7KT_)au#IKs`vtU%o4Y4Ea)ltE}kQD&&x zySEn)_+32EYT+VZV5{H_g~b~R?l*>^nD(wXhmSZu5~J>W@|y2zCsu68R-pGH z|0Hj+-GAjD`_%iQ$M1%+5@y}`nDUR9(!;&s!+pB3(GLcBI@sR&l(lAjPv<(iIPhb(@n{phpo#Ezc!uRy!@>{ zaMun;>u`}<3im%#tSZI957M+5#PtsPQl34Cdd>QZC7!@ynqS7J<$<%!GaNAG$w40> zNwjhK0{K4_1m7N5`~O3r4bRa30MH=yrfs)E)`vYG_{mDEIt8SQ^~p3=%pikl^TCz# zZo@3=bIgRq!$PAiOZ@~mAT(vE$sMX!F$oQKW$%cdF(HQG%A=_0U_4F1|An3FkfZTl z=#Pn^fMW|Wo<5Awd>h+$ce4^bC`C^F*`C3V50p{nfEIi+Z zi!d5-_=5qpe3(6jrnrcw`DO{gx}S=VZL=N@0rExCAK1i*IfPz&9igi}QOA6pGWUP+OY5$xUhu$L`yB=O_((2D(HNw`H9A%erb# zGfrL7;M&LXjp7-~R<(EN>imxGYXY9>@l|=|A8<06Wu*K~NT_ znF7>M#(y!;Y5jn!QJv+F-x!TYDU#)_$C?-*^D`|`sPDbYL)G~ktu~tQ7M7+woeR|8 zKFiy{GJnZUlb7$!1s8xTJoo_aWp(lOKR>j{r| zFD|q=F!du)-5$r2=&^(3PvUGMdyP^iVwlIwDsfp( zf2T~3Rz z8iLc*%zcM_Or2f1`eg{=Vt^-?KHE7eNVfW>s=Ddi-PV_QK#8j>U5d2@c#Pj%du&&Z z`L-$)MD*U6qSTmkXZxDn;voG7=3T$F-%51{BHV0D1+s?BAj7vt{ECb`(4HrchmVxrM!kGP zB%mHQN=W3wX`8=a{^O&D)xAQo6KLCG$CeLqY<;&V5_PIc+7Bp-5Q2%vx`?+K!m~e4 zYhhYP_%C#~hY^p6Rh+lB193Qw;{emr)EqWl_3FEQIf1ulu&-90iXC`X`>QW5_@M@n zXH2U`AbtaT$8T}Hm52{r$bh@eJ2YGt@C5*&K^aNVEmZE$D8)eWza_Bd(@~+ce?+ErjROWch-@nz3?(57R-JajCkGycj z0Exq=Ut82ADOdMQzi-B>;mcj#-mc~OXqsWMI9wWG8mhr zRi{>E=MrDh1 zCJDb6y!`1Xl89h6gZ+|(9cULbXe?Eh#w_UW)qY%%szJwWeEiBWXk8GypAwxpnnBFC z%1b}UCS0><J~Xn%W9O zPrgY6-@(XL%m&3e*12WT8r#4-CN}lTyLB$fzA&KBNF+GJw}wMBSx@-pa{J9N@1AuF7RI#+ri%Z$K5-MG#Md&^s4(eb=4;PsC&T-AxU*)}p z7nkv`t6^CSny3~Jd{MEACyMk=C#Od@PG& zGmQTzRPZU#;f93p(CkTJ63JJ_fV!z!WLel6;noL!7%IatJh@5;=s9U=mLR;D4HYR( zN({RV7?;87YB=gi{^XR+m3PQgQ~z_&$2!#;y9sDveQ%{^!>F-1pfM?mz}e;0!56G+ zqiAs}U+EWdk9m-r=W3YUOknwxZhr15Q^0E>sx>ZSRToErF6fy3AVNoav`Hz#SE*O= z-(3XSHmwEA?X{`6ill9lwEHE2&%_dAdK{q%=v9was07=#c6B~K2RLLL#|QvaWj7@7WOf%dZ*DbAEYkWLWJzJ$JmyGD{= zL$X3)R{qJfd^myGF_q_6FGw8c>ryG{z_||Swy=RGUqJ>;$m$sv9nL|wmcKhdTVQ>z zJ~n8ap+$GFm!$F*$QC}|k67El0|G9)=UX;UNeP1S@$f~q>zr_TAF_w&9LrlKT`fkuP|0)a4OWhB)=pl7(i_v0wf zfUla?B+0;ks9$8Xok1YX-%tM#5*aaxfsHsWAGBQ59n4+ajh)Ou>h@M&jGbM~^znxl zK_GIFtfZI*IBg%QXi41H)O(*0pTg_DV*D(ni_ktdDtNuH(ec^X1!;CH>N_JYOlroj z22n3;c|8JjVRn#R#!nlo6Tfvjj>Hf6nltTnsP%HkhEX^edtHCVL3t zfomL}EHvC&mbs`nFKMHE{hGNaUTwFi;FtWl{v)M*JuwJ{V(^g?#h*M>h&BvWj=qiz z_+CaiqQ~^}=fL}6M zFA~|G9K~d~Pg(eSeOy`Oem!1!=IS}|KfggsD{{RapXvK>U9l*NjVKoNnkv)lq|f-) z@9E$iQc7vOqj$&@aXKu?0~=KgBst0;wg)puc>mB)VtP8`cIqu#WKXrS(lGudZO`)n zY}FyIrPcScD6tbxN^caGzB$>8pc!-6fK#e_!Kk+n@B7>kXVIx>l{0LyiYzF7GSfuf zx)a$x5V(jqqw7Z3MHk#l1de^<-#=y4xs(nJbz-3nFKt6UG|1GL*uJL=#B-g4pI%d? z+;sGWlarAmHwh|HPgeEP%2ipPp*a6yBK^`eollU@P5JJi zA0^eQ!mrhzimqZBjOf8M|75V8IK z9F?$b*Wjq-hZArYh8@bInRAD+LF5JSHB)7K2h;-#dt;kLXZy$?fm$rkn&F%2p5>LK zWuMK~`<&eNjv(G;#r49{Dh1sjeE;d4FgI?~SBj{B8aM{8d#;9e33 zy8J#oy?%u>{FwmchwW%Tr7m;j4P0kju%%`q3P^=J*DlLag;lszMJ|*#StHQoH*mM_ z!%S3+hkXgoCc!e+dZmqsa`$ZZMER&L?EpQuQ-4 z74%)Jq2y3+b{5B8r*|@IQj8w1QDk@3J@8n^5@XjiW1>kGLILTl68v{;XcL`=QUG0B7Y;5S6skN`f&CPM7P-T zIMDMO-@O+#yZO@O=46uv3Do3yxFUZPVL;R_NWrsaG3;=6GI+v*LkacP7BZboSoh)%55Jw?M&~7Rgy+VQVB>@v-USuG3Z88^FGK_ zt0pH!!5MKu3?3f`)s37xtNNc=(HM@19=T|0N+y_og&bSqf!YNKU97gd8Ey|ubkKiq zMxD4+Qc2x-4dJo9Ko-ta9#RLP6jZ6oNZ&-&FExEgAVhB@L=S2>zUBhE z&fouK7R=NMSrVf63)2PqnFxL$T;1J3ndxpNSg|E=AtLj%Obv7ms39k0~n;Uw}#Lwy!@xWXwP*!LJP1>r0K%AsSLlQv6Ejw18Opx+~nB z{8^<6#meRFJf_itLULRqQ?`r!M?K9K0e}Z4eF8p-{F0Yocy_ zgJshB4fI4xDX+iZ4O@6LTf6(tsT7LS>fBaLi!>n8b8ugWT7rCO{WH3=9{Nx{dJ0(p zXW+AfKxu9K5Ys!(Y!_$kwtB%Uaeu}w)Nsn|nz-aiuV#|nJ7p%fvj|&qNi?LI&R>;B zmD5PkN4B_g!6a@zRVSj1+#9*kbo(a-0SG4+;EC|av$8jw*n$CPoQCjen8E@(Ki!&p z+jl>8L)Qtnry-#H@V5^!um0;#(veDWjq2xM3u_YlQvIp(0h>>Tu*Q3Jk;w5pk3*$c zW*L|3ZN!Z9>=}0zRs@hJ`PO@Wgb$(S>ZM8`-DNs6)0nV7`y0QUS?X_Uw6K0}wv22! zeD$8b(i(MG8x5PL(&B8o`=X12BlOHN=%XAXY`ads?IJ_w@C(SvymdwkG%>7rwi5OH zHi5;r`n-_aZMWltH!C_MVRJ1uf;57rIV}8V;d1Pzl}zD&P|%IiT;C;pZEFBU7vPY9M2!E$?drqe60G+fZu(wB}rfA5nzzV0F_sh#M_ zv|4)`&uUp_=@eDk`z?;?a;|%HE)TuO#K56ki?+;0KmEMy#>$g!5WmJR4vhmnhq|~5 zQFEgpgtlvt>}UB{L1mZm-N3cEvCo%_yPo1+qdm0QmD;z!^zO-Ax9%pfeG;Giy8gkk z#BPBkmF?ry>uI8KGalhK$kN34VIAyBf5EIR9Nabh^{%qQ)B#3u2kGm~^b!Nc>`qFG zz)gX3x`@J}Ka%pToq1ZhX=;mkzGFq#xB*-2dd$({P9U!CQTf70)845Q+O|@k1A^RPC-kWdRQzHe+xs6Ifyh)Re#}++{ zhR3m3SksXGc!;CxTHlv@j4XO$85-y}6b^&h2L!lnHfwTN6o2)_tv>T>N(%e!sX17D zjFKBC$NdX5QL8{{f5}E+Vv}5EFe48O33UQS@wIy;1U3=k4T^aCm)oD#aE9f$b=5#S zDG@Kkx`rDQZ6^r47&fY&Wzj!?eG0E7tc1$~H51gdze3)zmufZ~ANbP7rpfY{tJ6-g z47aCBml%VO$2ISiZ#wDyc;=&hzOzpqN#*HgBDDXV+aSC+&)7(^XoCt8a~M4hA+E=| zmvckfAP~6P(W0RDD&N@Rg^}mlbWw&48}x3wW^F>4jQMAX7S2v#GLuz#FMI42Lj`O* zNQw`!&;||NBGBR|i{iExLpjTo`6sQC^N3e`?X~sZAqol#UYF_b(WOHZOvcAb%tZ*W zBt2+|ClUkG$R1`MseWr<9mt>47HxbdosAA7LY$=yr7Qm&boTY^g)r%y6!2u2!NhDh zM!rFW)Z}U6&8ABK8grTUSLd*uLS@#(waug>- z-d9gW`k#ld=^6JR;^=7qt{f17X9v{fp-aeZFli#A~`WP#I5?MiUYVf*;FP?Q7VhJy`-JRQt33wkJ z9|TSDVI>6D)pPi9o`o=M|Bas#;a|>EK&gV&NRg(ByjT=@#uq=;X6d~U7yRHDFN>dR zC=zuqMLaeFvprCAf;>3jPmht$ICRw2U<=YXw8#UGv3zFNro%g{Enh(&gye$< zE4x9cSvfPf3(EaGIzI#E`yR43A8xZf{rsk@dTpYW4qhduA9i($#~^p|nSkLsDYsfL zTQWgywnXQ2;ChHeElwwDdp~X{vKqzO?&E`B-8ZT~v8xWgBKOH{8BnKf(Sp(p=vB>? zVXy5B0<@sCDL4C^^&sl;^kMS=`rTeo-2G6Km^%qpx%iv9a5Gac4d~!&?PBa{VKGz88Gc6<- zpg(@#OOD);`~8P!Jz)Lblg_drdhz90Hd|p`A}a7<8)ed@7l(&QB8d0dHm0eY$?olr zq1Y51ePJN3jgzWRP=m|uJ`bdoP?5RvYPH83GtOq!kN!-KjW_1gmV@LUZyjcE7N-l@*hugjUHCb{E#0<)RbJeu4XaF`P7@z!OF;60*|h2(Slqn`~W=~m0{}DX*}kvk~5-tia*klhds~p zvTrM3^!lx^xS^%S&+LdnlE2}pNt0e?BVphSPJU&%#VmOpG7xBoUqg4By(?anEQ#~R zTFm)FwH#Ku-Ihlp$Z$1d)9i!uhj_9fqzRNNX`M)9zIg6~IRUO}H*E*aT2`OArp+n$ zvb_>-N7wt1U~2aGU~}cF+5-b=GVlf2p+c$eoYp+3n)8gxXxuD$`c04Zy^Ms2f3Z!n z>-{S8mNL-~cyt~~!NsSw*c|MEB)L2_Zj^Vja2+~rhQxslHBHo=#kiq#`Hji$r%lNN zvsx8}1UA%ou0OblE`-mztOet65^Re!!^+s5yb2K zaun%Rlfq&EcIe3kTX)yRBIjK<CTDA%dZH4878} z==TZCE730pIk6;msX)Klu=rT!MUK!xt5i&MFt@DRI#q~_HGMu0&s#DoNxo#fF=V%B zkUzP^%&+jbHlftTaVTkc$);lPH(ZEf7i1H4H(T1?Ix<`=>f39&5rXKVbZWB>`$zZ*9 zl`j z3yC1fKp{iigsIaN4Q{j#61-IdLn;Kz(938ukKGgzl(Bf9`kfye@D~9wn#vYKrkFhL zUvnRtp}{WK>Jnvoi68T=r5UgNr+7h&G*+ac=e*Q^PsfQ`y)E2c_+vY=5KfFA`_oGf zCCca7P9P!Vty@wX4=H;JIu}LZ_$js|T`x~4n{wFmFHz|I@jP5OY479FQ8k<8EgX&# zt%4Inw8Ir-MoX6EzY8l=@7fbd$|bF&!LMcO9`t8swl(>ejWdBu#6or-qDoVH<}L5b zp*LDG=z^f!lt$~?DlLN59_0;bJ|3b8=nm9QPTsz~xsO#K;p~QG%W`S?xZ#AN1xYsN zHa=a5(P6~`{?ehz*4U-z9h97m zxywON3rY@;6k!@EI^*FQERamd84=nya%K4|zLQ2}H534+#gFnXaD(JecYah=dJ*nT zIzYfA!lxzk!#UmMN2VKZQbYaA!xV=-|M-jiZbmp!F;(P=DZQ8j)xQ0;;30HIW_4Z$ z=3Mq-*(60Ism%~^doxRl)HCLxY<)8Dx|2+5M%<@acW0q8m(TFWd`sP|p4rA+ef-sd zm^6fWjYd)qX!dV%eoF7n0<#v75%c~~mxu7tnk$ndjAV%eM$J`#LRh^_00L=DTERHy zUro`8Ouahe-Y7`^>^<+f^2icQ=k4Im1@b@3fxX_eRWd!AYynn1M$?hS!DsGshqkZ3 zNXVG>RiR}83uk`ZNX2jAlW2K0IsM~3=gT>1;X6ppXvuJkpVIkm-d$tDEAx)uTw4sH zmz|KR@+>*1W0;XS0{YanH)&a+?fJd83dyHl6;bs%<6>LK-4~bkga);PJhFrI;+4!; z^Y@n!M5DuG*%8LrZ7-ykifqkiPMPqIYkl6japV>+32e_B5)VNC3xJ8Q>)kZ#H?`1w zupgn(x2JNor?)zoP$p*D7pNVlf-d^F9me%b+HNwYZ5A-UiQCi@Nr%(IqMnC&8t}5h zek~_XI9Uvgp7=&x!-T6XD8w8X_wA3LJ`K55=e)yPL$*W5r&}Usvo1zm`yLJRv%G3y zeN!c%?jSIez{ca|tOZxK`^@FhM1|r<$!E|)G}wVcIjhWU%yuXDriP6#m5H7OkbaA0k zD3;ko>_*ukp((#?`d!LTy_HVKhoXwoYN!uNmzotlDtZJ6(Ltg$eD46YmB}g+{F6y% zc4i~dus<+PjvlD)x$D6R32u-EfaCme{2a)>s7I+Ir^Yk;lUm@Z2^3!AqZI2nDCvx07?A{{@ zxi3JJ{fycj+lh+l?`w0jiE&=KIO9BB*H(-Xn^y7U9+~(*?fd=mdie2f7y|W~v43iJ zZ&>W*A&jgJ>*-wY*VgCj9cCYK1ce?}PL5U{EeL&fryiZ#Gk^mrWbeyQtoJ7}7AU3P z0Q$R>)RVF z5$u1zlB2hIr&RUmd3ivRcXoe!8HjBw_;dsg+rOiCta)eDpcdc9j7Yw}SpOWQrvB|A ztc`~Vt4@4~*B0yPL!pv(b{XY9;dNa79rl{`hx`VwMG(-O@7WkLjiNt~uYl`Q$`G_# z$_?LtA_rr+vra9*>3}O?T3;{7^y#0&avNH!NS6do8};^Z3F`UyaNqWDUJmnpB+*(s zJ{=Yzt9wEbe=1k7e6OD8;o|oJ=fy^@NMQ)?3BZ6N3i_7ZoMes$M^cEvlV}LZ1uJ6|<)!(8j@KMzM z>vaW4rj$~Elh~K-S_Uv}01LG4a^UTymDiE#C=sG3ym$UZ2hX?xH!sVZww77QgF=(# zOW={=SbIK??03wHq5fgT{FF+)Mc_mBDdQqGyD%#*hHvZJ1+omFLJJ9!=fh{}8ASOg|eC_ohqZ<0o?09RUj_i#=w90hb z^9J&Un$8{4NF2&RsSqPLRWAYJV}&s$_2;`X&yG{;{wIyM4Juq0BP;>(OUsCk7HWjj zG-NggmAVc$a=kg25C%o~YU>dPgy{O;rrcf>hmXPY!G^FD2n)(^!IZSc)B?}mKI}

id;v7gX(W7557CR53bh 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 literal 0 HcmV?d00001 diff --git a/pictures/zookeeper_分布式读写锁.png b/pictures/zookeeper_分布式读写锁.png new file mode 100644 index 0000000000000000000000000000000000000000..a18e2a5e94790bdb8b83542c3af52e3102f62473 GIT binary patch literal 11198 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 literal 0 HcmV?d00001 diff --git a/pictures/zookeeper_分布式锁_临时节点方法.png b/pictures/zookeeper_分布式锁_临时节点方法.png new file mode 100644 index 0000000000000000000000000000000000000000..aadf7a94bfd41d15a317559ea3588ffdab395233 GIT binary patch literal 13362 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 Q#tMQYL}g*c!ulWnKUh}zVE_OC literal 0 HcmV?d00001