一, 微服务的高可用
在注册中心, 配置中心高可用方案之前, 了解一下注册中心的工作原理, 下面分为两个部分来解释, 一是注册中心和各个微服务的注册表的获取与同步, 二是注册中心如何去维护注册表.
1.1, 注册表的获取与同步
Eureka Server 和 Eureka Client 之间的关系, 通过注册表来维护, 而注册表的通过 Eureka Server 集中化管理, 每个 Client 在本地进行注册表的缓存, 通过周期性的任务拉取最新的注册表信息. 简单的示例图如下.
根据上图所展示的流程, 可以了解到注册中心与微服务之间的基本联系的流程:
1.服务 A 启动时, 向 Eureka Server 注册自己的相关信息
2.当服务 B 向 Eureka Server 拉取最新的注册表时, 就可以拿到服务 A 的一台机器注册信息
3.服务 A 的另外两台机器再去注册, 服务 B 30s 后再次去拉取时, 就会得到服务 A 的三台机器的注册信息
4.服务 A, 每 30s 向 Eureka Server 发送一次心跳信息, 表明自己的注册信息还是有效的
以上是注册中心与微服务之间交互的大体流程, 在具体的实践中, Eureka Server 会提供多级缓存, 其中的注册表的信息的获取与同步, 又会有细微的差别.
1.Eureka Server 的注册表直接基于纯内存, 即在内存里维护了一个数据结构.
2.各个服务的注册, 服务下线, 服务故障, 全部会在内存里维护和更新这个注册表.
3.各个服务每隔 30 秒拉取注册表的时候, Eureka Server 就是直接提供内存里存储的有变化的注册表数据给他们就可以了.
4.同样, 每隔 30 秒发起心跳时, 也是在这个纯内存的 Map 数据结构里更新心跳时间.
Eureka Server 的注册表是纯内存处理的, 因此处理速度会很快, 同时提供 readWriteCacheMap 和 readOnlyCacheMap 做缓存, 保障了频繁读写不会冲突. 示意图如下.
上图介绍了 Eureka Server 多级缓存的工作原理:
1.当第一台服务 A 注册时, 它的注册信息会更新到内存的注册表中, 如果 readWriteCacheMap 中有相应的信息, 则过期掉, 如果没有则不做操作
2.当服务 B 去拉取注册表信息时, 先找 readOnlyCacheMap , 没有再找 readWriteCacheMap , 再没有就去内存的注册表查找注册信息, 查到就更新到 readWriteCacheMap 中, 返回给服务 B, 服务 B 的注册表中, 就会有一台服务 A 的机器注册信息
3.readOnlyCacheMap 和 readWriteCacheMap 之间的同步是有一个后台的定时任务, 每隔 30s 去同步一次, 缓存同步任务
4.第二台服务 A 注册时, 更新内存的注册表, 同时把 readWriteCacheMap 过期掉
5.在缓存同步任务执行之前服务 B 去拉取注册表时, 都是从 readOnlyCacheMap 中拿到数据, 新的注册表的信息, 不会被服务 B 拿到
6.30s 后, 缓存同步任务会同步 readWriteCacheMap 和 readOnlyCacheMap 中的数据, 把 readOnlyCacheMap 中的注册表过期掉, 这时服务 B 就会找 readWriteCacheMap 拿数据, readWriteCacheMap 从内存中拿到数据后缓存, 返回给服务 B, 服务 B 的注册表中, 就会有两台服务 A 的机器注册信息
7.在下一个 30s, 缓存同步任务把 readWriteCacheMap 同步到 readOnlyCacheMap 之前, readOnlyCacheMap 没有第二台服务 A 的注册缓存, 因此都是从 readWriteCacheMap 中取到最新数据
注:
readOnlyCacheMap 缓存更新的定时器时间间隔, 默认为 30 秒
readWriteCacheMap 缓存过期时间, 默认为 180 秒
由以上流程说明可知, Eureka Server 采取了多级缓存策略, 同时最新的注册表生效有 30s 的时延. 多级缓存机制的优点是什么:
1.尽可能保证了内存注册表数据不会出现频繁的读写冲突问题.
2.并且进一步保证对 Eureka Server 的大量请求, 都是快速从纯内存走, 性能极高.
1.2, 注册中心维护微服务的注册表
Eureka Client 与注册表相关的行为如下所示:
1.服务注册 (Registry)-- 初始化时执行一次, 向服务端注册自己服务实例节点信息包括 ip, 端口, 实例名等, 基于 POST 请求.
2.服务续约 (renew)-- 默认每隔 30s 向服务端 PUT 一次, 保证当前服务节点状态信息实时更新, 不被服务端失效剔除.
3.更新已经注册服务列表 (fetchRegistry)-- 默认每隔 30s 从服务端 GET 一次增量版本信息, 然后和本地比较并合并, 保证本地能获取到其他节点最新注册信息.
4.服务下线 (cancel)-- 在服务 shutdown 的时候, 需要及时通知服务端把自己剔除, 以避免客户端调用已经下线的服务.
Eureka Client 是通过 Jersey Client 基于 Http 协议与 Eureka Server 交互来注册服务, 续约服务, 取消服务, 服务查询等. 同时, Server 端还会维护一份服务实例清单, 并每隔 90s 对未续约的实例进行失效剔除.
Eureka Server 有一个自我保护机制, 当网络发生故障时, 客户端与服务端不通, 这是需要启动 Eureka Server 的自我保护机制, 这样不会剔除服务, 当网络恢复时, 退出自我保护. 自我保护有两个参数, 最后一分钟收到的心跳数 (Renews (last min)), 期望收到的心跳数 (Renews threshold), 当 Renews threshold> Renews (last min) 时, 进入自我保护模式.
Renews (last min) = 实例数 * 2 #实例数算上 Eureka Server 自注册服务
Renews threshold = Renews (last min) * 0.85 # 0.85 可配置
下图的注册中有 10 个实例:
推荐多个 Eureka Server 部署时, 开启自我保护
eureka.client.register-with-eureka = true
1.3, 分布式注册中心
了解了注册中心的工作原理, 下面开始研究分布式服务, 多注册中心, 多服务实例的情况.
当微服务仅向一台注册中心注册时, 当这个注册中心发生故障时, 新服务无法继续注册上去, 旧服务的注册信息, 缓存在其他注册中心和客户端中, 依旧可以使用, 当重启之后, 无法向注册中心注册, 也是无法使用的.
因此构建高可用的注册中心时, 需要交叉注册, 每个注册中心既当服务端, 又当客户端, 向其他注册中心注册自己, 同时微服务需要向每个注册中心进行注册, 由注册中心自己过滤互备, 防止单个注册中心故障而导致只往它上面注册微服务重启后不可用. 示意图如下所示.
目前注册中心与配置中心集中在一起, 可拆可不拆, 对整体影响不大, 拆分是为了注册中心和配置中心相互间不影响. GitLab 部署在某一台机器上, 所有 config 共用, 由于 GitLab 的原因, 导致 config 的分布式存在单点故障的隐患. 每个 config 分别用独立的 GitLab, 又给运维带来极大的不便. 后期采用 apollo, 用数据库存储配置, 利用数据库的分布式优势替代 GitLab, 来解决单点故障的问题.
1.4, 注册中心压测
根据压测调研, 8 核 4G 的 Eureka Server 在处理 1000 个服务实例时, 没有任何压力, 在默认情况下, 可以处理 7000 个实例, 超出的会超时报错, 在修改 tomcat 的配置之后, 最多可以承载 8000 实例, 此时 CPU 基本满载.
升级注意事项:
1,Eureka Server 之间相互注册, Eureka Client 需要在每个 Server 上都注册一边
2,Eureka Server 开启自我保护
3,Eureka Client 的实例数不超过 1000 个
参考:
[1] https://www.jianshu.com/p/ae4f0c8b8135
[2] https://www.cnblogs.com/xishuai/p/spring-cloud-eureka-safe.html
[3] http://springcloud.cn/view/31
来源: https://www.cnblogs.com/lossingdawn/p/11223375.html