目录
一、分布式存储简介
二、etcd介绍
三、etcd架构
四、etcd集成实践
一、分布式存储简介
随着云原生与容器化技术的兴起,分布式系统的复杂性大大增加。分布式系统面临一系列问题,比如部署复杂、响应时间慢、运维复杂等,其中最根本的问题是多个节点之间的数据共享问题。这就需要一个可靠的共享的存储系统来同步信息,这时就出现了分布式存储系统,这也是其产生的背景。
二、etcd介绍
etcd 就是一个分布式存储的中间件,使用 Go 语言编写,并通过Raft协议确保分布式数据一致性,解决了分布式数据一致性问题。
etcd 目前有两个主要的大版本系列:V2 和 V3。V2 版本是较早的版本,提供了一系列简单的 API 来进行键值对的存储、获取、修改、删除等操作。V2 实例最初是基于纯内存实现的,意味着所有的数据都不会持久化到磁盘上,这限制了其在需要数据持久化的场景下的适用性。V3 版本是对V2的重大升级,引入了许多改进,包括更强的API、更好的性能、数据的一致性和持久化存储能力。V3 引入了事务支持,可以在一次操作中执行多个修改操作,保证这些操作的原子性。它使用了Raft一致性算法的优化版本,增强了集群的稳定性和容错能力。
为什么需要 etcd 呢?所有的分布式系统,都面临一个问题就是多节点之间数据共享问题,这个和团队协作的道理是一样的,成员可以分头干活,但是总需要共享一些信息。而 etcd 就是这样一个服务,用来共享同步信息。
etcd 提供了如下能力:
- etcd 提供存储以及获取数据的接口,它通过 Raft 协议保证 etcd 集群中多个节点数据的强一致性。
- 提供监听机制,客户端可以监听某个 key 或者某些 key,用于监听和推送变更。
- 提供 key 的过期及续约机制,客户端通过定时刷新来实现续约,用于集群监控以及服务注册发现。
很多开源组件中都有使用 etcd 组件,如 Kubenetes、Service Mesh、TiDB 中的 PD等等,通过 etcd 来保证其核心功能的正常工作。
三、etcd架构
etcd 的架构设计围绕以下几个组件展开,旨在提供一个高可用、强一致的分布式键值存储系统。
- HTTP Server:这是 etcd 与外部世界的主要交互层,负责接受来自客户端的 API 请求(如请求、读取、更新、删除等操作),以及处理集群内部的同步心跳信息。它支持HTTP/1.1和gRPC协议,后者是etcd v3版本中推荐的通信方式。
- raft模块:Raft 是 etcd 实现一致性保证的核心算法,它负责维护集群成员间的共识,确保数据的一致性和领导选举。每个 etcd 节点都是 Raft 协议中的一个成员,通过选举出领导者来处理所有客户端的写请求,并将更改复制到其他节点,保证数据的强一致性。
- WAL:持久化日志模块,在 etcd 中,所有数据修改在提交到存储之前,都会先写入到预写日志(WAL)。这是一种持久化机制,即使在系统崩溃时也能保证数据不丢失。
- Snapshot:etcd 会周期性的创建快照,以减少 WAL 文件数量,提高恢复速度。
- MVCC:MVCC机制允许etcd在不影响并发读操作的情况下处理写请求。它为每个键值对维护了一个版本链,每个写操作都会生成一个新的版本,从而支持历史版本查询和并发控制。
- gRPC Server:gRPC Server是etcd v3中用于处理客户端请求的高层框架,它提供了一种高效、跨语言的RPC机制,支持复杂的API调用如范围查询、事务处理等,且具有良好的性能和可扩展性。
四、etcd集成实践
本地已经通过 Docker 安装了 etcd 的镜像,通过 Docker 启动 etcd 服务,命令如下:
docker run -it --name etcd-server -p 23790:2379 -p 23800:2380 -e ALLOW_NONE_AUTHENTICATION=yes -d bitnami/etcd
然后可以通过接口确认是否启动成功,可以检查一下版本,http://localhost:23790/version
返回的结果为:{etcdserver: "3.5.1",etcdcluster: "3.5.0"}。
添加 etcd 依赖,项目其他依赖请自行添加
<dependency>
<groupId>io.etcd</groupId>
<artifactId>jetcd-core</artifactId>
<version>0.5.0</version>
</dependency>
编写 etcd 配置类
@Configuration
public class EtcdConfig {
@Bean
public Client client() {
// 这里简化直接写上etcd地址,应该写到配置中
return Client.builder().endpoints("http://localhost:23790").build();
}
@Bean
public KV kvClient(Client client) {
return client.getKVClient();
}
}
etcd 操作接口
@RestController
public class EtcdController {
@Autowired
private KV kvClient;
@RequestMapping("/etcd/get/data")
public String getData(@RequestParam String key) throws ExecutionException, InterruptedException {
GetResponse response = kvClient.get(ByteSequence.from(key.getBytes()), GetOption.DEFAULT).get();
return new String(response.getKvs().get(0).getValue().getBytes());
}
@RequestMapping("/etcd/put/data")
public String putData(@RequestParam String key, @RequestParam String value) throws ExecutionException, InterruptedException {
PutResponse response = kvClient.put(ByteSequence.from(key.getBytes()), ByteSequence.from(value.getBytes())).get();
return response.toString();
}
}
然后启动服务调用相关接口,就能看到相应的操作结果。
往期经典推荐
Sentinel与Nacos强强联合,构建微服务稳定性基石的重要实践-CSDN博客
Raft共识算法领导者选举流程揭秘-CSDN博客
Raft日志复制技术及成员变更原来是这样的_raft 日志-CSDN博客
云原生:Kubernetes下的Java应用部署实战详解_kubernetes 创建java微服务-CSDN博客
决胜高并发战场:Redis并发访问控制与实战解析_redis并发控制-CSDN博客