(译)分布式系统的模式
看到网友推荐这篇博客,整理归纳一下 https://martinfowler.com/articles/patterns-of-distributed-systems/
我发现有人翻译了,但是翻译的不全。 https://xie.infoq.cn/article/f4d27dd3aa85803841d050825
这里的分布式系统是指所有的系统,共性问题
Type of platform/framework | Example |
---|---|
Databases数据库 | Cassandra, HBase, Riak |
Message Brokers消息队列 | Kafka, Pulsar |
Infrastructure基础架构元信息管理 | Kubernetes, Mesos, Zookeeper, etcd, Consul |
In Memory Data/Compute Grids网格 | Hazelcast, Pivotal Gemfire |
Stateful Microservices微服务 | Akka Actors, Axon |
File Systems文件系统 | HDFS, Ceph |
面临的共性问题
- 进程崩溃
- 配置文件修改导致的暂时下线(大公司已经出过很多起了)
- 异常场景没处理,比如磁盘满
- 云场景下的其他影响的场景
这些场景下如何处理数据丢失?解决方案:WAL
- 网络延迟问题
- 不知道其他进程的状态是否正常 解决方案: 心跳
- 脑裂导致的数据冲突 解决方案 Quorum
-
另一种异常,进程暂停,可能是内部在忙,没有及时响应,可能是gc引起的延迟等等
-
Generation Clock 我的理解就是term 推进,老master检测自己不是最新的index,就自动降低身份 也就是Lamport’s timestamp
-
时间同步问题,ntp是不准的甚至是会出错的,有原子钟方案,也有lamport逻辑时钟方案
(其实原子钟方案比较简单,一个gps原子钟七八万,我之前的老项目用过,这个成本对于互联网公司还好吧,为啥都不用呢,不方便部署么)
一个整合方案,以及涉及到具体的设计细节
WAL
首先是WAL 也就是commit log commit log要保证持久性
每个日志要有独立的标记,依此来分段整理,方便写,但是不能无限长,所以要有个Low-Water-Mark标记,其实就是后台线程定期删日志
日志更新就相当于队列追加写了,为了吞吐可能要异步一些
考虑如何实现这样一个kv;
首先kv得能序列化成log,并且能从log恢复
需要支持指定snapshot/timestap恢复,这两种是淘汰判定的标准,也就是一个unit
Low-Water-Mark实现方案
- 基于快照snapshot,每次成功的写入index都是一次快照,快照落盘就过期,可以删掉 有点像生产消费
zookeeper etcd都是这个方案,代码类似下面
public SnapShot takeSnapshot() {
Long snapShotTakenAtLogIndex = wal.getLastLogEntryId();
return new SnapShot(serializeState(kv), snapShotTakenAtLogIndex);
}
//Once a snapshot is successfully persisted on the disk, the log manager is given the low water mark to discard the older logs.
List<WALSegment> getSegmentsBefore(Long snapshotIndex) {
List<WALSegment> markedForDeletion = new ArrayList<>();
List<WALSegment> sortedSavedSegments = wal.sortedSavedSegments;
for (WALSegment sortedSavedSegment : sortedSavedSegments) {
if (sortedSavedSegment.getLastLogEntryId() < snapshotIndex) {
markedForDeletion.add(sortedSavedSegment);
}
}
return markedForDeletion;
}
- 基于时间,有点像日志轮转
kafka就是这种方案
private List<WALSegment> getSegmentsPast(Long logMaxDurationMs) {
long now = System.currentTimeMillis();
List<WALSegment> markedForDeletion = new ArrayList<>();
List<WALSegment> sortedSavedSegments = wal.sortedSavedSegments;
for (WALSegment sortedSavedSegment : sortedSavedSegments) {
if (timeElaspedSince(now, sortedSavedSegment.getLastLogEntryTimestamp()) > logMaxDurationMs) {
markedForDeletion.add(sortedSavedSegment);
}
}
return markedForDeletion;
}
private long timeElaspedSince(long now, long lastLogEntryTimestamp) {
return now - lastLogEntryTimestamp;
}
读取?一致性方案 quorum
读大多数 判定数据
写WAL - 复制组
复制组要保证高可用性 心跳检查,节点间心跳以及自身心跳处理,如果自身僵住需要退位
复制组有主从,涉及到选举算法 zab raft之类