用 etcd 实现 API 网关
文章目录
应用场景
etcd 是一个高可用强一致性的键值仓库,在很多分布式系统架构中得到了广泛的应用,其最经典的使用场景就是服务发现。
etcd 的场景默认处理的数据都是系统中的控制数据。所以etcd在系统中的角色不是其他NoSQL产品的替代品,更不能作为应用的主要数据存储。etcd中应该尽量只存储系统中服务的配置信息,对于应用数据只推荐把数据量很小,但是更新和访问频次都很高的数据存储在etcd中。
主要作用
etcd 主要提供可靠的配置共享、服务发现和服务注册。
可用于实现微服务网关,相当于一个反向代理服务器。
etcd 提供的分布式并发原语:互斥锁、读写锁、Leader 选举、队列、栅栏、事务。
特点
- 简单:易于部署,易使用。基于 HTTP+JSON 的 API 让你用 curl 就可以轻松使用。
- 安全:可选 SSL 客户认证机制。
- 快速:每个实例每秒支持一千次写操作。
- 可信:使用一致性 Raft 算法充分实现了分布式。(Etcd 是基于 Raft 协议作为分布式一致性算法来解决领导者选举和日志复制问题)
系统中实现服务注册与发现所需的基本功能
- 服务注册:同一 service 的所有节点注册到相同目录下,节点启动后将自己的信息注册到所属服务的目录中。
- 健康检查:服务节点定时发送心跳,注册到服务目录中的信息设置一个较短的 TTL,运行正常的服务节点每隔一段时间会去更新信息的 TTL。
- 服务发现:通过名称能查询到服务提供外部访问的 IP 和端口号。比如网关代理服务时能够及时的发现服务中新增节点、丢弃不可用的服务节点,同时各个服务间也能感知对方的存在。
核心组件
从 etcd 的架构图中我们可以看到,etcd 主要分为四个部分。
- HTTP Server:用于处理用户发送的 API 请求以及其它 etcd 节点的同步与心跳信息请求。
- Store:用于处理 etcd 支持的各类功能的事务,包括数据索引、节点状态变更、监控与反馈、事件处理与执行等等,是 etcd 对用户提供的大多数 API 功能的具体实现。
- Raft:Raft 强一致性算法的具体实现,是 etcd 的核心。
- WAL:Write Ahead Log(预写式日志),是 etcd 的数据存储方式。除了在内存中存有所有数据的状态以及节点的索引以外,etcd 就通过 WAL 进行持久化存储。WAL 中,所有的数据提交前都会事先记录日志。Snapshot 是为了防止数据过多而进行的状态快照;Entry 表示存储的具体日志内容。
通常,一个用户的请求发送过来,会经由 HTTP Server 转发给 Store 进行具体的事务处理,如果涉及到节点的修改,则交给 Raft 模块进行状态的变更、日志的记录,然后再同步给别的 etcd 节点以确认数据提交,最后进行数据的提交,再次同步。
服务发现
服务发现要解决的是分布式系统中最常见的问题之一,即在同一个分布式集群中的进程或服务,要如何才能找到对方并建立连接。本质上来说,服务发现就是想要了解集群中是否有进程在监听 udp 或 tcp 端口,并且通过名字就可以查找和连接。要解决服务发现的问题,需要有下面三大支柱,缺一不可。
- 一个强一致性、高可用的服务存储目录。基于 Raft 算法的 etcd 就是一个强一致性高可用的服务存储目录。
- 一种注册服务和监控服务健康状态的机制。用户可以在 etcd 中注册服务,并且对注册的服务设置 key TTL,定时保持服务的心跳以达到监控健康状态的效果。
- 一种查找和连接服务的机制。通过在 etcd 指定的主题(由服务名称构成的服务目录)下注册的服务也能在对应的主题下查找到。
服务注册
etcd 可以提前知道服务器是否有单点故障或网络问题,然后不再转发流量到有问题的服务器
租约模式
etcd 是一个 分布式键值存储系统,在一个集群中,如果一个节点配置了某些属性,集群中的每个节点都可以使用完整的存档,我们每次在网关节点后的计算节点集群加一个服务,只需要向etcd 注册 该服务(其实就是 存一个值)然后向etcd 发送心跳,当etcd 没有检测到心跳就会 把这个键值对 删了(这整个动作是etcd里的租约模式),网关那边 就只需要 watch 这个 key ,就能够知道 所有服务的所有动态了。
启动服务端
./etcd1 –listen-client-urls ‘http://0.0.0.0:2379’ –advertise-client-urls ‘http://0.0.0.0:2379’
|
|
交互命令
|
|
续租(租约模式)
租约被删除,归属的 kv 也同时会被删除。
|
|
用 etcd 实现 API 网关的部分源代码
注意事项
- API 网关实现了服务和路由的注册与发现
- API 网关为什么要初始化路由,因为可以实现路由权限校验、利用中间件判断路由归属哪个服务并执行转发等
- 匹配服务对应路由规则:需要考虑固定路由、动态路由(如/test/:id/tes2/:id)、通配符(如/test//id/)
- 代码生成的 lease id 是 10 进制,终端命令生成的 lease id 是 16 进制
注册服务和路由,需考虑部署多服务器或 k8s 多节点的情况
- 注册 service: 使用 sync.Map 存储 2 对 kv,第一个 key 为 微服务名称,第二个 key 为 lease id,value 均为微服务对应的 host
- 注册 route: 使用 sync.Map 存储 2 对 kv,第一个 key 为 method+path,第二个 key 为 lease id, value 均为微服务名称
|
|