Skip to content

Commit d06864f

Browse files
committed
[docs update]zookeeper content perfection
1 parent 5780c46 commit d06864f

18 files changed

+112
-104
lines changed

docs/database/mysql/mysql-query-cache.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ mysql> show variables like '%query_cache%';
7676
我们这里对 8.0 版本之前`show variables like '%query_cache%';`命令打印出来的信息进行解释。
7777

7878
- **`have_query_cache`** 该 MySQL Server 是否支持查询缓存,如果是 YES 表示支持,否则则是不支持。
79-
- **`query_cache_limit`**MySQL 查询缓存的最大查询结果,查询结果大于该值时不会被缓存。
80-
- **`query_cache_min_res_unit`**查询缓存分配的最小块的大小(字节)。当查询进行的时候,MySQL 把查询结果保存在查询缓存中,但如果要保存的结果比较大,超过 `query_cache_min_res_unit` 的值 ,这时候 MySQL 将一边检索结果,一边进行保存结果,也就是说,有可能在一次查询中,MySQL 要进行多次内存分配的操作。适当的调节 `query_cache_min_res_unit` 可以优化内存。
81-
- **`query_cache_size`**为缓存查询结果分配的内存的数量,单位是字节,且数值必须是 1024 的整数倍。默认值是 0,即禁用查询缓存。
82-
- **`query_cache_type`**设置查询缓存类型,默认为 ON。设置 GLOBAL 值可以设置后面的所有客户端连接的类型。客户端可以设置 SESSION 值以影响他们自己对查询缓存的使用。
79+
- **`query_cache_limit`** MySQL 查询缓存的最大查询结果,查询结果大于该值时不会被缓存。
80+
- **`query_cache_min_res_unit`** 查询缓存分配的最小块的大小(字节)。当查询进行的时候,MySQL 把查询结果保存在查询缓存中,但如果要保存的结果比较大,超过 `query_cache_min_res_unit` 的值 ,这时候 MySQL 将一边检索结果,一边进行保存结果,也就是说,有可能在一次查询中,MySQL 要进行多次内存分配的操作。适当的调节 `query_cache_min_res_unit` 可以优化内存。
81+
- **`query_cache_size`** 为缓存查询结果分配的内存的数量,单位是字节,且数值必须是 1024 的整数倍。默认值是 0,即禁用查询缓存。
82+
- **`query_cache_type`** 设置查询缓存类型,默认为 ON。设置 GLOBAL 值可以设置后面的所有客户端连接的类型。客户端可以设置 SESSION 值以影响他们自己对查询缓存的使用。
8383
- **`query_cache_wlock_invalidate`** :如果某个表被锁住,是否返回缓存中的数据,默认关闭,也是建议的。
8484

8585
`query_cache_type` 可能的值(修改 `query_cache_type` 需要重启 MySQL Server):

docs/database/mysql/mysql-questions-01.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ InnoDB 的性能比 MyISAM 更强大,不管是在读写混合模式下还是
228228
229229
## MySQL 索引
230230
231-
MySQL 索引相关的问题比较多,对于面试和工作都比较重要,于是,我单独抽了一篇文章专门来总结 MySQL 索引相关的知识点和问题: [MySQL 索引详解](./mysql-index.md) 。
231+
MySQL 索引相关的问题比较多,对于面试和工作都比较重要,于是,我单独抽了一篇文章专门来总结 MySQL 索引相关的知识点和问题: [MySQL 索引详解](https://javaguide.cn/database/mysql/mysql-index.html) 。
232232
233233
## MySQL 查询缓存
234234

docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-in-action.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ root@eaf70fc620cb:/apache-zookeeper-3.5.8-bin# cd bin
3636

3737
如果你看到控制台成功打印出如下信息的话,说明你已经成功连接 ZooKeeper 服务。
3838

39-
![](./images/连接ZooKeeper服务.png)
39+
![连接 ZooKeeper 服务](https://oss.javaguide.cn/github/javaguide/distributed-system/zookeeper/connect-zooKeeper-service.png)
4040

4141
### 2.3. 常用命令演示
4242

@@ -163,7 +163,7 @@ numChildren = 1
163163

164164
Curator 是Netflix公司开源的一套 ZooKeeper Java客户端框架,相比于 Zookeeper 自带的客户端 zookeeper 来说,Curator 的封装更加完善,各种 API 都可以比较方便地使用。
165165

166-
![](./images/curator.png)
166+
![](https://oss.javaguide.cn/github/javaguide/distributed-system/zookeeper/curator.png)
167167

168168
下面我们就来简单地演示一下 Curator 的使用吧!
169169

docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-intro.md

+45-50
Large diffs are not rendered by default.

docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-plus.md

+26-36
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,7 @@
22

33
> [FrancisQ](https://juejin.im/user/5c33853851882525ea106810) 投稿。
44
5-
## 1. 好久不见
6-
7-
离上一篇文章的发布也快一个月了,想想已经快一个月没写东西了,其中可能有期末考试、课程设计和驾照考试,但这都不是借口!
8-
9-
一到冬天就懒的不行,望广大掘友督促我🙄🙄✍️✍️。
10-
11-
> 文章很长,先赞后看,养成习惯。❤️ 🧡 💛 💚 💙 💜
12-
13-
## 2. 什么是ZooKeeper
5+
## 什么是ZooKeeper
146

157
`ZooKeeper``Yahoo` 开发,后来捐赠给了 `Apache` ,现已成为 `Apache` 顶级项目。`ZooKeeper` 是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。其一致性是通过基于 `Paxos` 算法的 `ZAB` 协议完成的。其主要功能包括:配置维护、分布式同步、集群管理、分布式事务等。
168

@@ -34,7 +26,7 @@
3426

3527
比如各个分布式组件如何协调起来,如何减少各个系统之间的耦合度,分布式事务的处理,如何去配置整个分布式系统等等。`ZooKeeper` 主要就是解决这些问题的。
3628

37-
## 3. 一致性问题
29+
## 一致性问题
3830

3931
设计一个分布式系统必定会遇到一个问题—— **因为分区容忍性(partition tolerance)的存在,就必定要求我们需要在系统可用性(availability)和数据一致性(consistency)中做出权衡** 。这就是著名的 `CAP` 定理。
4032

@@ -44,7 +36,7 @@
4436

4537
而上述前者就是 `Eureka` 的处理方式,它保证了AP(可用性),后者就是我们今天所要讲的 `ZooKeeper` 的处理方式,它保证了CP(数据一致性)。
4638

47-
## 4. 一致性协议和算法
39+
## 一致性协议和算法
4840

4941
而为了解决数据一致性问题,在科学家和程序员的不断探索中,就出现了很多的一致性协议和算法。比如 2PC(两阶段提交),3PC(三阶段提交),Paxos算法等等。
5042

@@ -56,7 +48,7 @@
5648

5749
而为什么要去解决数据一致性的问题?你想想,如果一个秒杀系统将服务拆分成了下订单和加积分服务,这两个服务部署在不同的机器上了,万一在消息的传播过程中积分系统宕机了,总不能你这边下了订单却没加积分吧?你总得保证两边的数据需要一致吧?
5850

59-
### 4.1. 2PC(两阶段提交)
51+
### 2PC(两阶段提交)
6052

6153
两阶段提交是一种保证分布式系统数据一致性的协议,现在很多数据库都是采用的两阶段提交协议来完成 **分布式事务** 的处理。
6254

@@ -86,7 +78,7 @@
8678
* **阻塞问题**,即当协调者发送 `prepare` 请求,参与者收到之后如果能处理那么它将会进行事务的处理但并不提交,这个时候会一直占用着资源不释放,如果此时协调者挂了,那么这些资源都不会再释放了,这会极大影响性能。
8779
* **数据不一致问题**,比如当第二阶段,协调者只发送了一部分的 `commit` 请求就挂了,那么也就意味着,收到消息的参与者会进行事务的提交,而后面没收到的则不会进行事务提交,那么这时候就会产生数据不一致性问题。
8880

89-
### 4.2. 3PC(三阶段提交)
81+
### 3PC(三阶段提交)
9082

9183
因为2PC存在的一系列问题,比如单点,容错机制缺陷等等,从而产生了 **3PC(三阶段提交)** 。那么这三阶段又分别是什么呢?
9284

@@ -104,13 +96,13 @@
10496

10597
所以,要解决一致性问题还需要靠 `Paxos` 算法⭐️ ⭐️ ⭐️ 。
10698

107-
### 4.3. `Paxos` 算法
99+
### `Paxos` 算法
108100

109101
`Paxos` 算法是基于**消息传递且具有高度容错特性的一致性算法**,是目前公认的解决分布式一致性问题最有效的算法之一,**其解决的问题就是在分布式系统中如何就某个值(决议)达成一致**
110102

111103
`Paxos` 中主要有三个角色,分别为 `Proposer提案者``Acceptor表决者``Learner学习者``Paxos` 算法和 `2PC` 一样,也有两个阶段,分别为 `Prepare``accept` 阶段。
112104

113-
#### 4.3.1. prepare 阶段
105+
#### prepare 阶段
114106

115107
* `Proposer提案者`:负责提出 `proposal`,每个提案者在提出提案时都会首先获取到一个 **具有全局唯一性的、递增的提案编号N**,即在整个集群中是唯一的编号 N,然后将该编号赋予其要提出的提案,在**第一阶段是只将提案编号发送给所有的表决者**
116108
* `Acceptor表决者`:每个表决者在 `accept` 某提案后,会将该提案编号N记录在本地,这样每个表决者中保存的已经被 accept 的提案中会存在一个**编号最大的提案**,其编号假设为 `maxN`。每个表决者仅会 `accept` 编号大于自己本地 `maxN` 的提案,在批准提案时表决者会将以前接受过的最大编号的提案作为响应反馈给 `Proposer`
@@ -119,7 +111,7 @@
119111
120112
![paxos第一阶段](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cd1e5f78875b4ad6b54013738f570943~tplv-k3u1fbpfcp-zoom-1.image)
121113

122-
#### 4.3.2. accept 阶段
114+
#### accept 阶段
123115

124116
当一个提案被 `Proposer` 提出后,如果 `Proposer` 收到了超过半数的 `Acceptor` 的批准(`Proposer` 本身同意),那么此时 `Proposer` 会给所有的 `Acceptor` 发送真正的提案(你可以理解为第一阶段为试探),这个时候 `Proposer` 就会发送提案的内容和提案编号。
125117

@@ -135,7 +127,7 @@
135127

136128
> 对于 `Learner` 来说如何去学习 `Acceptor` 批准的提案内容,这有很多方式,读者可以自己去了解一下,这里不做过多解释。
137129
138-
#### 4.3.3. `paxos` 算法的死循环问题
130+
#### paxos 算法的死循环问题
139131

140132
其实就有点类似于两个人吵架,小明说我是对的,小红说我才是对的,两个人据理力争的谁也不让谁🤬🤬。
141133

@@ -147,15 +139,15 @@
147139

148140
那么如何解决呢?很简单,人多了容易吵架,我现在 **就允许一个能提案** 就行了。
149141

150-
## 5. 引出 `ZAB`
142+
## 引出 `ZAB`
151143

152-
### 5.1. `Zookeeper` 架构
144+
### Zookeeper 架构
153145

154146
作为一个优秀高效且可靠的分布式协调框架,`ZooKeeper` 在解决分布式数据一致性问题时并没有直接使用 `Paxos` ,而是专门定制了一致性协议叫做 `ZAB(ZooKeeper Atomic Broadcast)` 原子广播协议,该协议能够很好地支持 **崩溃恢复**
155147

156148
![Zookeeper架构](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/07bf6c1e10f84fc58a2453766ca6bd18~tplv-k3u1fbpfcp-zoom-1.image)
157149

158-
### 5.2. `ZAB` 中的三个角色
150+
### ZAB 中的三个角色
159151

160152
和介绍 `Paxos` 一样,在介绍 `ZAB` 协议之前,我们首先来了解一下在 `ZAB` 中三个主要的角色,`Leader 领导者``Follower跟随者``Observer观察者`
161153

@@ -165,7 +157,7 @@
165157

166158
`ZAB` 协议中对 `zkServer`(即上面我们说的三个角色的总称) 还有两种模式的定义,分别是 **消息广播****崩溃恢复**
167159

168-
### 5.3. 消息广播模式
160+
### 消息广播模式
169161

170162
说白了就是 `ZAB` 协议是如何处理写请求的,上面我们不是说只有 `Leader` 能处理写请求嘛?那么我们的 `Follower``Observer` 是不是也需要 **同步更新数据** 呢?总不能数据只在 `Leader` 中更新了,其他角色都没有得到更新吧?
171163

@@ -185,7 +177,7 @@
185177

186178
定义这个的原因也是为了顺序性,每个 `proposal``Leader` 中生成后需要 **通过其 `ZXID` 来进行排序** ,才能得到处理。
187179

188-
### 5.4. 崩溃恢复模式
180+
### 崩溃恢复模式
189181

190182
说到崩溃恢复我们首先要提到 `ZAB` 中的 `Leader` 选举算法,当系统出现崩溃影响最大应该是 `Leader` 的崩溃,因为我们只有一个 `Leader` ,所以当 `Leader` 出现问题的时候我们势必需要重新选举 `Leader`
191183

@@ -229,13 +221,13 @@
229221

230222
![崩溃恢复](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/99cdca39ad6340ae8b77e8befe94e36e~tplv-k3u1fbpfcp-zoom-1.image)
231223

232-
## 6. Zookeeper的几个理论知识
224+
## Zookeeper的几个理论知识
233225

234226
了解了 `ZAB` 协议还不够,它仅仅是 `Zookeeper` 内部实现的一种方式,而我们如何通过 `Zookeeper` 去做一些典型的应用场景呢?比如说集群管理,分布式锁,`Master` 选举等等。
235227

236228
这就涉及到如何使用 `Zookeeper` 了,但在使用之前我们还需要掌握几个概念。比如 `Zookeeper`**数据模型****会话机制****ACL****Watcher机制** 等等。
237229

238-
### 6.1. 数据模型
230+
### 数据模型
239231

240232
`zookeeper` 数据存储结构与标准的 `Unix` 文件系统非常相似,都是在根节点下挂很多子节点(树型)。但是 `zookeeper` 中没有文件系统中目录与文件的概念,而是 **使用了 `znode` 作为数据节点**`znode``zookeeper` 中的最小数据单元,每个 `znode` 上都可以保存数据,同时还可以挂载子节点,形成一个树形化命名空间。
241233

@@ -264,13 +256,13 @@
264256
* `numChildre`:该节点的子节点个数,如果为临时节点为0。
265257
* `pzxid`:该节点子节点列表最后一次被修改时的事务ID,注意是子节点的 **列表** ,不是内容。
266258

267-
### 6.2. 会话
259+
### 会话
268260

269261
我想这个对于后端开发的朋友肯定不陌生,不就是 `session` 吗?只不过 `zk` 客户端和服务端是通过 **`TCP` 长连接** 维持的会话机制,其实对于会话来说你可以理解为 **保持连接状态**
270262

271263
`zookeeper` 中,会话还有对应的事件,比如 `CONNECTION_LOSS 连接丢失事件``SESSION_MOVED 会话转移事件``SESSION_EXPIRED 会话超时失效事件`
272264

273-
### 6.3. ACL
265+
### ACL
274266

275267
`ACL``Access Control Lists` ,它是一种权限控制。在 `zookeeper` 中定义了5种权限,它们分别为:
276268

@@ -280,19 +272,19 @@
280272
* `DELETE`:删除子节点的权限。
281273
* `ADMIN`:设置节点 ACL 的权限。
282274

283-
### 6.4. Watcher机制
275+
### Watcher机制
284276

285277
`Watcher` 为事件监听器,是 `zk` 非常重要的一个特性,很多功能都依赖于它,它有点类似于订阅的方式,即客户端向服务端 **注册** 指定的 `watcher` ,当服务端符合了 `watcher` 的某些事件或要求则会 **向客户端发送事件通知** ,客户端收到通知后找到自己定义的 `Watcher` 然后 **执行相应的回调方法**
286278

287279
![watcher机制](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ac87b7cff7b44c63997ff0f6a7b6d2eb~tplv-k3u1fbpfcp-zoom-1.image)
288280

289-
## 7. Zookeeper的几个典型应用场景
281+
## Zookeeper的几个典型应用场景
290282

291283
前面说了这么多的理论知识,你可能听得一头雾水,这些玩意有啥用?能干啥事?别急,听我慢慢道来。
292284

293285
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dbc1a52b0c304bb093ef08fb1d4c704c~tplv-k3u1fbpfcp-zoom-1.image)
294286

295-
### 7.1. 选主
287+
### 选主
296288

297289
还记得上面我们的所说的临时节点吗?因为 `Zookeeper` 的强一致性,能够很好地在保证 **在高并发的情况下保证节点创建的全局唯一性** (即无法重复创建同样的节点)。
298290

@@ -306,7 +298,7 @@
306298

307299
总的来说,我们可以完全 **利用 临时节点、节点状态 和 `watcher` 来实现选主的功能**,临时节点主要用来选举,节点状态和`watcher` 可以用来判断 `master` 的活性和进行重新选举。
308300

309-
### 7.2. 分布式锁
301+
### 分布式锁
310302

311303
分布式锁的实现方式有很多种,比如 `Redis` 、数据库 、`zookeeper` 等。个人认为 `zookeeper` 在实现分布式锁这方面是非常非常简单的。
312304

@@ -330,13 +322,13 @@
330322

331323
具体怎么做呢?其实也很简单,你可以让 **读请求监听比自己小的最后一个写请求节点,写请求只监听比自己小的最后一个节点** ,感兴趣的小伙伴可以自己去研究一下。
332324

333-
### 7.3. 命名服务
325+
### 命名服务
334326

335327
如何给一个对象设置ID,大家可能都会想到 `UUID`,但是 `UUID` 最大的问题就在于它太长了。。。(太长不一定是好事,嘿嘿嘿)。那么在条件允许的情况下,我们能不能使用 `zookeeper` 来实现呢?
336328

337329
我们之前提到过 `zookeeper` 是通过 **树形结构** 来存储数据节点的,那也就是说,对于每个节点的 **全路径**,它必定是唯一的,我们可以使用节点的全路径作为命名方式了。而且更重要的是,路径是我们可以自己定义的,这对于我们对有些有语意的对象的ID设置可以更加便于理解。
338330

339-
### 7.4. 集群管理和注册中心
331+
### 集群管理和注册中心
340332

341333
看到这里是不是觉得 `zookeeper` 实在是太强大了,它怎么能这么能干!
342334

@@ -352,11 +344,9 @@
352344

353345
![注册中心](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/469cebf9670740d1a6711fe54db70e05~tplv-k3u1fbpfcp-zoom-1.image)
354346

355-
## 8. 总结
356-
357-
看到这里的同学实在是太有耐心了👍👍👍,如果觉得我写得不错的话点个赞哈。
347+
## 总结
358348

359-
不知道大家是否还记得我讲了什么😒。
349+
看到这里的同学实在是太有耐心了👍👍👍不知道大家是否还记得我讲了什么😒。
360350

361351
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/912c1aa6b7794d4aac8ebe6a14832cae~tplv-k3u1fbpfcp-zoom-1.image)
362352

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<mxfile host="Electron" modified="2023-03-17T03:27:16.817Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/20.3.0 Chrome/104.0.5112.114 Electron/20.1.3 Safari/537.36" etag="0ANlqol-jA5SFaGvqXnS" version="20.3.0" type="device"><diagram id="JzN1fFsxfL2nXP1EqGl9" name="Page-1">7Vrfc5s4EP5r9HgZQAiLR7DN3fSuc53Lw7VPHRVkzAUjV8iJ3b/+JBC/iYlTO3ZmmszE0mq1Et+utN8SAzjf7H/nZLv+yCKaAsuI9gAugGWZpuHIDyU5lJIZdktBzJNIKzWC++QH1UJDS3dJRPOOomAsFcm2KwxZltFQdGSEc/bUVVuxtLvqlsR0ILgPSTqU/ptEYl1KsTVr5H/QJF5XK5uOfr5vJHyIOdtlej1gwcAJggCXwxtS2dIPmq9JxJ5aIrgEcM4ZE2Vrs5/TVGFbwVbOC54ZrffNaSZeMmE/u//7wyHA8wP9fvhq/vmPTb7/pq3k4lDhQSMJj+4yLtYsZhlJl43UD3f8kSqjpuwUABQ9Q/aaCX8xttUq/1EhDtrxZCeYFK3FJtWjK5YJPWg6sp8Lzh5qV0iU/HKLal/PPnn1GGzHQ3rkcZEOMMJjKo7ozWr/yLinbEMFP8h5nKZEJI/dfRAdgHGt1zhBNrQfTvCJdXWfSFz54bOaf4eq7hdtrugs9p3eQfdu0pf4mr6Ev3x5Tl86P+nLYqrHOTm0FLYsyUTesvxJCaSCznEQ6htcZzi7f9H29E37qL5slDtooqp+lNcHmn31QLvJgHHfQ8Ag5L59wGgEH0m6oxWBCVIWPuSDSGpCQ/n5aZ0Ier8lhX+eJCk8HgarJE3nLGW8sAUjQvEqrMOjGslYRusIeaRc0P3xGBn6VE+wcBd8iPTReGrYnWlonXWL2dnOhXKAcwTp4tO4KOKGgSEOLoi424t2R2Wa60I+m4J8yHvPCfnctpGNLhnk/RRzA5jjKcyHvPZdYQ7x7WHuTmGevW/MkXV7mFcvLjqg97lXFnnq3UQDRkTydY16C2El/0SEoDwrJJYBR7lT2xP4BC41hLYFHBrBrZKdRpEGnMbuZwUMuyZKiqdnHSNH5oShkgMODJ2LJ5nm2BlLondOkxz71miSORseo8kS5mrl8fCEdqqplx7PF5Q6pr5eJmsdNO7wy9Y6yOjFETxe6/Rro57+ZWodE78itG7jNcxFq+gXh5b5zGXyxrE1UUc77lH9C8WWNZYfzpscRpNAL2OskPrVk1vy8uc8ScNGXYDrMuR6SQNeg4x1b4Nbp2IOdivSXHEoF1aSU+nYiLHZwNilKdnYG5VfDLznqVn/Nqzq11Ndjs0JQ5d291iV+7y7w5TkeRJ2ndw9sRPkje4T8bnV/tKkbdlrErXqVHm6E11nT9FXuzp69bflvDKGHNg39LZVnDVSxS0d4BnAd8ESA+wBLCUz9dd3B+ElU6boBtRkStYikiaxuldC6Xkq5b5KwElIUk8PbJIoKvjmGCfohm2fFpwho/fLQFleDzK6MxJf0Hg+lH7un98jdEo5KgA4AEsb+Bh43ojHli7AEPgLpexipV8qy7uqnmUZPzIWUWDNR0zWg3ICUiPahA88VBidA/XNFiMrNOqNIOAtgLxpR2LISVXMfOOyFavWxKLtByjWkrZdu/skGPizQoKAvwTYVhLPBB5Wy3pSzXvpsqxetzRePwouNmAr+9Km6xWrSIlc0VANvADespq1KDYghWaxN9lARUMuJ6t5w9zc8EkKyCZJVdh/oMLnJMlkJWJ8lLhUJ626mtdCbOUYghI4pKi9/KMU8ruYsTilZJvkdyHbFANhXqgGq9K6bLbtI8tvr3DesyzpeecsI2fIzuu3ae3DjNHJh1l2m28ulZd08/UwuPwf</diagram></mxfile>

0 commit comments

Comments
 (0)