Skip to content

Commit

Permalink
更新 分布式相关内容
Browse files Browse the repository at this point in the history
  • Loading branch information
0xcaffebabe committed Mar 16, 2020
1 parent 0ddf49c commit afd8b85
Show file tree
Hide file tree
Showing 9 changed files with 277 additions and 156 deletions.
2 changes: 2 additions & 0 deletions SUMMARY.md
Expand Up @@ -85,6 +85,8 @@
- [架构](./软件工程/架构/架构.md)
- [编程范式](./软件工程/架构/编程范式.md)
- [设计原则](./软件工程/架构/设计原则.md)
- [系统设计](./软件工程/架构/系统设计/系统设计.md)
- [分布式](./软件工程/架构/系统设计/分布式.md)
- [组件构建原则](./软件工程/架构/组件构建原则.md)
- [软件架构](./软件工程/架构/软件架构.md)
- [实现细节](./软件工程/架构/实现细节.md)
Expand Down
Binary file added assets/202031620440.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/202031620538.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
83 changes: 1 addition & 82 deletions 中间件/分布式/Zookeeper.md
Expand Up @@ -51,7 +51,7 @@ watcher注册与异步通知机制,能够很好的实现分布式环境下不
- PERSISTENT
- PERSISTENT_SEQUENTIAL(发生重复会自增)
- EPHEMERAL
- EPHEMERAL_SEQUENTIAL
- EPHEMERAL_SEQUENTIAL(发生重复会自增)
- 节点Znode可以包含数据和子节点(但是EPHEMERAL类型的节点不能有子节点)

## JAVA操作
Expand Down Expand Up @@ -161,87 +161,6 @@ public static String getServer() {
}
```

## 实现分布式锁

### 解决方案

- 数据库
- redis
- zookeeper
- 实现简单,失效时间容易控制
- SpringCloud内置全局锁

### 原理

多个jvm同时在zookeeper.上创建同一个相同的节点(/lock) , 因为zookeeper节点是唯一的,如果是唯一的话,那么同时如果有多个客户端创建相同的节点/lock的话,最终只有看谁能够快速的抢资源,谁就能创建/lock节点,这个时候节点类型应该使用临时类型。

当一个JVM释放锁后(关闭zk连接),临时节点会被删除,等待锁的其他JVM会收到节点被删除的通知,这些等待的JVM会重新参与到竞争

需要注意的是,要根据业务设置锁等待时间,避免死锁

### 实现

- 上锁

```java
public void lock() {
// 尝试获取锁,如果成功,就真的成功了
if (tryLock()) {
System.out.println(Thread.currentThread().getName() + "获取锁成功");
// 否则等待锁
} else {
waitLock();
// 当等待被唤醒后重新去竞争锁
lock();
}
}
private boolean tryLock() {
try {
// 通过zk创建临时节点的成功与否来表示是否获得锁
zkClient.createEphemeral("/lock");
return true;
} catch (Exception e) {
return false;
}
}
private void waitLock() {
// 监听节点被删除的事件
zkClient.subscribeDataChanges("/lock", new IZkDataListener() {
@Override
public void handleDataDeleted(String s) throws Exception {
// 如果节点被删除,唤醒latch
if (latch != null) {
latch.countDown();
}
}
});
// 如果zk有lock这个锁
if (zkClient.exists("/lock")) {
// 在这里进行等待,直至被上面的事件监听唤醒
latch = new CountDownLatch(1);
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 等待完成删除所有监听事件,避免监听器堆积影响性能
zkClient.unsubscribeAll();
}
```

- 释放锁

```java
public void release() {
if (zkClient != null) {
// 关闭zk客户端,临时节点也随之被删除,相当于释放锁,让其他人去竞争
zkClient.close();
System.out.println(Thread.currentThread().getName()+"释放锁完成");
}
}
```

## master选举

### 原理
Expand Down
10 changes: 0 additions & 10 deletions 中间件/消息队列/rabbitMQ.md
Expand Up @@ -309,16 +309,6 @@ args.put("deadRoutingKey", deadRoutingKey);
Queue queue = new Queue("user_queue", true, false, false, args);
```

## 解决分布式事务

MQ解决分布式事务原理: 采用最终一致性原理

- 生产者一定要将数据投递到MQ服务器中(消息确认机制)
- MQ消费者消息能够正确消费消息,采用手动ACK模式(当消费者消费消息失败,则不确认消息,消息进行重试)
- 当生产者出错回滚,发送到补偿队列的消息会检测生产者的数据是否提交成功,如果没有,则补偿队列的消费者会重新执行一遍生产者没有提交的事务

![批注 2020-03-16 164628](/assets/批注%202020-03-16%20164628.png)

## 消息可靠投递方案

![批注 2019-07-21 113405](/assets/批注%202019-07-21%20113405.png)
65 changes: 1 addition & 64 deletions 数据库系统/Redis.md
Expand Up @@ -291,67 +291,4 @@ slaveof 127.0.0.1 6379

![2020225163638](/assets/2020225163638.png)

原理同HashMap

## 实现分布式锁

### 原理

1.获取锁的时候,对某个key执行setnx,加锁(如果设置成功(获得锁)返回1,否则返回0),并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。

2.获取锁的时候还设置一个获取的超时时间(防止死锁),若超过这个时间则放弃获取锁。

3.释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放

### 实现

```java
public class RedisLock {

private StringRedisTemplate template;

private static final String LOCK_KEY = "LOCK";

private String identifyValue;

public RedisLock(StringRedisTemplate template) {this.template = template;}

/**
* @param acquireTimeout 获取锁之前的超时时间
* @param expireTime 锁的过期时间
* @return
*/
public boolean lock(long acquireTimeout, long expireTime) {
// 获取锁的时间
long inTime = System.currentTimeMillis();
identifyValue = UUID.randomUUID().toString();
for (; ; ) {
// 判断获取锁是否超时
if (System.currentTimeMillis() - inTime >= acquireTimeout) {
return false;
}
// 通过setnx的方式来获取锁
if (template.opsForValue().setIfAbsent(LOCK_KEY, identifyValue, expireTime, TimeUnit.MILLISECONDS)) {
// 获取锁成功
return true;
}
// 获取锁失败,继续自旋
}
}

public void release() {
if (identifyValue == null){
throw new IllegalStateException("没有获取锁");
}
// 删除的时候验证value,必须确保释放的锁是自己创建的
if (!identifyValue.equals(template.opsForValue().get(LOCK_KEY))){
throw new IllegalStateException("锁的value不一致");
}
template.delete(LOCK_KEY);
}
}
```

### 与zookeeper比较

相对比来说Redis比Zookeeper性能要好,从可靠性角度分析,Zookeeper可靠性比Redis更好。因为Redis有效期不是很好控制,可能会产生有效期延迟
原理同HashMap
60 changes: 60 additions & 0 deletions 数据库系统/分布式事务.md
Expand Up @@ -11,22 +11,62 @@
![批注 2019-10-31 194221](/assets/批注%202019-10-31%20194221.png)

- 一致性(Consistency):服务A、B、C三个结点都存储了用户数据, 三个结点的数据需要保持同一时刻数据一致性。
- 对系统的一个数据更新成功之后,如果所有用户都能够读取到最新的值,该系统就被认为具有强一致性
- 可用性(Availability):服务A、B、C三个结点,其中一个结点宕机不影响整个集群对外提供服务,如果只有服务A结 点,当服务A宕机整个系统将无法提供服务,增加服务B、C是为了保证系统的可用性。
- 对于用户的每一个操作请求总是能够在有限的时间内返回结果
- 分区容忍性(Partition Tolerance):分区容忍性就是允许系统通过网络协同工作,分区容忍性要解决由于网络分区导致数据的不完整及无法访问等问题。

最多只能同时满足其中两项

![202031017918](/assets/202031017918.png)

分布式系统中,分区容忍性必不可少,因为需要总是假设网络是不可靠的,所以CAP理论实际上是要在可用性和一致性之间做权衡

- 保证一致性(CP),不能访问未同步完成的节点,也就失去了部分可用性
- 保证可用性(AP),允许读取所有节点的数据,但是数据可能不一致

## BASE理论

- BA:(Basically Available ),基本可用
- 分布式系统在出现故障的时候,保证核心可用,允许损失部分可用性
- S:( Soft State),软状态,状态可以在一段时间内不同步
- 允许系统不同节点的数据副本之间进行同步的过程存在时延
- E:(Eventually Consistent ),最终一致,在一定的时间窗口内, 最终数据达成一致即可

## 柔性事务与刚性事务

- 柔性事务满足BASE理论(基本可用,最终一致)
- 刚性事务满足ACID理论

## Paxos

对多个节点产生的值,该算法能保证只选出唯一一个值

### 节点类型

- 提议者(Proposer):提议一个值
- 接受者(Acceptor):对每个提议进行投票
- 告知者(Learner):被告知投票的结果,不参与投票过程

![202031620538](/assets/202031620538.jpg)

## Raft

分布式一致性协议,主要是用来竞选主节点

有三种节点:Follower、Candidate 和 Leader。Leader 会周期性的发送心跳包给 Follower。每个 Follower 都设置了一个随机的竞选超时时间,一般为 150ms~300ms,如果在这个时间内没有收到 Leader 的心跳包,就会变成 Candidate,进入竞选阶段

当 Candidate获得超过半数票时,就成为Leader节点
如果有多个Candidate获得相同的票数,则重新开始投票
每个节点设置的随机竞选超时时间不同,因此下一次再次出现多个 Candidate 并获得同样票数的概率很低

### 数据同步

- 自客户端的修改都会被传入 Leader。此时该修改还未被提交,只是写入日志中
- Leader 会把修改复制到所有 Follower
- Leader 会等待大多数的 Follower 也进行了修改,然后才将修改提交
- 此时 Leader 会通知的所有 Follower 让它们也提交修改,此时所有节点的值达成一致

## 解决方案

### 两阶段提交(2PC)
Expand All @@ -39,6 +79,10 @@

- 优点:实现强一致性
- 缺点:整个事务的执行需要由协调者在多个节点之间去协调
- 所有事务参与者在等待其它参与者响应的时候都处于同步阻塞等待状态,无法进行其它操作
- 协调者如果发生故障会造成很大影响
- 当在提交阶段网络发生异常,只有部分参与者commit了消息,造成数据不一致
- 任意一个节点失败就会导致整个事务失败

### 三阶段提交(3PC)

Expand All @@ -59,6 +103,22 @@

![批注 2019-10-31 201018](/assets/批注%202019-10-31%20201018.png)

#### 本地消息表

- 在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中
- 之后将本地消息表中的消息转发到消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发
- 分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作

![202031620440](/assets/202031620440.png)

#### 补偿的方式

- 生产者一定要将数据投递到MQ服务器中(消息确认机制)
- MQ消费者消息能够正确消费消息,采用手动ACK模式(当消费者消费消息失败,则不确认消息,消息进行重试)
- 当生产者出错回滚,发送到补偿队列的消息会检测生产者的数据是否提交成功,如果没有,则补偿队列的消费者会重新执行一遍生产者没有提交的事务

![批注 2020-03-16 164628](/assets/批注%202020-03-16%20164628.png)

## LCN

### 原理
Expand Down

0 comments on commit afd8b85

Please sign in to comment.