(一):分布式系统如何实现服务注册发现
----- 学习笔记 -----
在上节开篇部分介绍微服务时,提到微服务架构的引进也带来了一些问题。其中服务注册发现,是最先需要解决的问题。简单来说,服务注册与发现就是保证当服务上下线发生变更时,服务消费者和服务提供者能够保持正常通信。
服务注册发现
首先,请你设想一下,在单体服务架构中,我们只有一个服务,这个服务前面就是像 Nginx 这样的网关系统,负责负载均衡,而后端的机器节点也是我们手动配置上去的,比如:
upstream backend {
server 10.0.0.1:80;
server 10.0.0.2:80;
}
相信大家都配置过此类配置,如果机器不够用了,增加一个节点,然后 reload 一下 Nginx 就好了。这样的配置,架构运行得还算稳定,维护成本公司也还可以接受。
但随着这个项目越来越出名,老板开始想继续开发 2 期、3 期、4 期……此时不断扩大的新项目仍旧和老项目共用一个用户体系,这么多服务使用同一个数据库,数据库压力也越来越大,你想到了把用户模块独立成一个单独的服务。这样也就开始了单体服务演进到微服务的过程。
这时,你遇到了第一个问题:单体服务和新拆出来的用户服务之间如何通信?其实你选择一个 RPC 协议或者 HTTP 做通信协议都是可以解决问题的。
那如何保证服务的高可用呢?你可能很容易想到,和单体服务一样,给这个新的用户服务配置一个内部网关用作负载均衡。因为内部服务数量不多,这样比较容易应付增加机器带来的网关配置变动。
随着项目越来越复杂,修正 Bug 和正确地添加新功能变得更加困难,你选择继续拆分服务。慢慢地,你拆分的服务数量上升到了两位数,但是你发现每次因为机器负载瓶颈而增加机器时,需要修改很多份内网网关配置, 这无形中可以预料到,修改配置带来的维护成本和出错的概率都会呈指数级增加。
这个时候我们亟需一种解决上述问题的办法:服务注册发现。
单说服务注册发现这个概念,你可能很难理解,但跟你说清楚它能解决什么问题,你就能很好理解了。为了方便你理解,我结合下面的图片做个简单说明。
服务注册发现示意图
服务注册中心
具体怎样利用服务注册发现解决多个服务间的通信问题呢?举一个简单的例子,你刚进入一个新公司尚未认识其他部门同事,但是你的业务完成需要跨部门协作,这时你该怎么联系这些人?
一般来说你的公司会用钉钉或者是飞书,整个公司有一个通讯录,组织架构中的所有联系人都会在列表中,当你联系其他部门同事时,就可以通过这个列表私聊他们。你用这个列表解决了与其他部门成员通信的问题,而解决多个服务间通信问题的工具,我们称之为服务注册中心。
你看,想解决多个服务间的通信问题,只要实现一个注册中心服务,让业务服务在启动的时候调用注册接口注册上来,再由注册中心服务把注册上来的机器信息存储起来,当其他服务调用时,通过 watch 服务名发现这个服务的后端机器节点,就可以了。
当你完全理解了注册中心后,你可以很容易实现一个简单的注册中心。但作为微服务最核心的组件,想要做到工业级产品,生产高可用,并不是那么容易的一件事。
注册中心的健康检查设计
想要实现一个基本的注册中心,健康检查功能是必不可少的。你可以想象一下,如果一个节点出现了问题,流量还被打过去,这肯定是所有业务都无法接受的,所以我们需要有一定的健康检查机制来确保服务节点的健康状态。
我们来看看 3 种不同的健康检查方式,你也可以结合具体的业务场景想一想哪种健康检查方式更适合自己。
1. 服务主动探活
服务通过定时发送续租信息到注册中心,以表明自己节点的存活。
主动探活的方式
优势:其实是我们在使用注册中心时用得最多的一种方式,如果你的服务集群规模不大,或者选用了类似 Eureka 这样的最终一致性的注册中心,服务主动探活绝对是你的最优的选择。这种方式最大程度避免了在Kubernetes环境中,因为 IP 重用导致节点在旧的服务上依然存活的问题,毕竟续租信息都是带着服务信息上报到注册中心的。
缺点:但主动探活的最大问题,是造成注册中心的写操作变多。特别是在服务发布时,节点会产生比较大的变动,注册中心的写压力也就会变大。而且强一致性的注册中心,节点变化一定要主节点确认,如果没有做注册中心的读写分离,就会产生大量的通知事件,对带宽、CPU 来说都是灾难性的问题,这个时候注册中心已经完全没有办法响应 TTL 的租约请求,也会导致大量的节点失效。
另外这种方案还有一个问题,主动租约,其实并不足以说明服务是健康的,毕竟有些情况下,服务虽然无法对外提供服务了,但还是可以对外发送租约请求的。
2. 注册中心主动发起健康检查
服务在进行服务注册时,向注册中心表明自己的健康检查接口,比如 /ping 或者 TCP 端口,注册中心通过定时访问的方式,探明节点是否存活。
优势:第二种方案在一定程度上解决了服务主动探活并不能说明服务健康的问题,毕竟通过 /ping 这种健康检查接口很大程度上可以说明服务的健康度。在Kubernetes环境中,也是通过对 Pod 进行主动健康检查来判定 Pod 的健康度的。
缺点:但这个方案也有一些问题,比如上面提到的 IP 重用问题,如果两个服务都用了 /ping 接口做健康检查,并且端口一致,就很容易发生节点在旧服务被重新激活的问题。当然也有相应的解决方案,就是参考 Envoy 做服务名称的 check。
3. 注册中心不进行任何健康检查,由调用方负载均衡器进行健康检查
注册中心不进行任何探活机制,全部由调用方的负载均衡器进行主动和被动探活。
第三种方案就比较极端了,不做任何健康检查,完全靠负载均衡器的能力。这种方式也是有应用场景的,如果使用了 gRPC 这样比较完善的 RPC 库,一般都有自动摘除节点的能力。但我们也要考虑到这个方案的不足,如果 IP 被重用,节点很大概率会一直存在在旧服务中,这样的脏数据随时都是风险点。
当然我们可以结合前面两个方案,优化此方案。你可以在做健康检查的同时,注册中心下发包含健康节点和非健康节点的数据到服务节点,并针对健康检查未通过的删除节点设置一个较长的过期时间,这样就可以解决 IP 重用产生脏数据的问题了。
在实际工作中,虽然第三种方案并没有很受大家欢迎,但优化过后的第三种方案,却是稳定性最高的方案,也是最容易实现的方案。
其实三种方式各有利弊,你在选择时,一定要根据自己的服务规模、运维环境,选择一个合适的方案。
注册中心选型
说了这么多,我们来看看注册中心的选型问题,毕竟选择一个合适的注册中心会事半功倍,也会减少线上异常的概率。
首先我们先看下几种注册中心的对比:
简单解释一下 CAP 理论,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance),CAP 不可能都取,只能取其中2个。
在注册中心这个场景中,一致性要求并不是很高,只要达到最终的一致性即可。毕竟涉及节点的注册和反注册,我们通知到客户端,也需要一定时间,一致性本身就是几乎不可能达到的事情。
所以在选型的时候,优先选择 AP 的系统。如果技术栈是 Go ,但又担心 Java 的组件不好维护,你也可以考虑自研注册中心,当然 CP 的注册中心并非不可用,在服务集群规模比较小的情况下,也是可以选择的。
结语
这一讲主要讲了在微服务架构中为什么要引入服务注册发现、注册中心健康检查的设计和注册中心的选型。
在这里还要补充一句,在做服务注册发现的时候,一定要坚持一个原则——不要陷入过度重视时效性的误区。作为一个程序员,大家肯定特别重视程序的性能,这也容易陷入推送时效性的竞赛中,但在注册中心这个场景,保证微服务集群的稳定性是第一优先级。
本节内容到这里就结束了,下一小节会讲解 Service Mesh 中的注册中心、注册中心使用过程中会遇到哪些问题,以及具体的解决措施。希望你学习完下一小节的内容,能够更好地解决注册中心使用中遇到的问题。
关于服务发现的经典问题+专家回答:
1,如何做到servicemesh 的平滑升级、服务的平滑重启过程?
专家回复:
对于 HTTP 协议这种连接阻塞协议,多数人不能回答出正确的流程,
比如先关闭 KeepAlive,处理完此连接上的请求直接关闭连接,再关闭监听器;
对于 HTTP/2 这种多路复用协议,无法回答出 goaway 帧的作用: HTTP/2 依赖
goaway 帧让客户端关闭连接,以达到重建连接的目的;而上述问题,是实现
Service Mesh 中 sidecar 平滑重启的关键。
2,对比传统微服务架构,如何理解service mesh sidecar数据面的实现?
专家回复:
sidecar 的编写,也就是类似网关层的编写,和传统的服务框架差别比较
大。整体来说是需要理解类似 xDS 的规范,比如 listener、cluster、router
等概念。从代码层面理解整个实现方式,根据配置预先创建 router,并绑定
LB/Handler 业务请求过来时,只需要匹配预先创建好的 router,拿到对应的
LB/Handler 等中间件即可,而不是像传统的微服务框 LB 等组件都是初次请求
动态创建的。
3,推荐使用 AP,为啥 k8s 使用 etcd 呢?
专家回复:
在大规模的k8s集群场景下,etcd也是已知的性能瓶颈点,很多公司已经魔改etcd,
或者采用其他的存储方式了。由于etcd的瓶颈问题,k8s的集群规模也受到限制,
现在比较常见的是搭建多套k8s集群。
4,是过期时间到之前,一直由调用方发送健康检查,如果正常,继续使用,不正常再
检查,直到过期时间到了,删除该节点,后续请求不再给到该节点?该方案健康检查的
粒度是到端口吧,如果IP漂移,还是会有IP重用产生脏数据问题吧?
设置过期时间的意义是什么:
“你可以在做健康检查的同时,注册中心下发包含健康节点和非健康节点的数据到服务
节点,并针对健康检查未通过的删除节点设置一个较长的过期时间,这样就可以解决
IP 重用产生脏数据的问题了。”
讲师回复:
这里需要结合上下文理解,这里主要还是解决对比数据的问题,这里需要结合前面的2
和3方案一起看。ip重用的问题,还是需要2方案中的check服务名解决。只是这个方
案同时下发了健康和不健康的节点,对于客户端来说可以做的策略更加灵活,比如客户
端的健康检查会更准确不会遗漏被注册中心因为网络分区问题错误过滤的节点。
5,现在大家不都用的zk,etcd,为这里推荐AP类型的呢?
讲师回复:
“CP 的注册中心并非不可用,在服务集群规模比较小
的情况下,也是可以选择的”。Eureka 典型的 AP,作为分布式场景下的服务发现的产
品较为合适,服务发现场景的可用性优先级较高,一致性并不是特别致命。其次 CP
类型的场景 Consul,也能提供较高的可用性,并能 k-v store 服务保证一致性。
而Zookeeper、Etcd则是CP类型牺牲可用性,在服务发现场景并没太大优势。
nacos好像可以CP、AP
讲师回复:是的nacos可以选择cp或者ap模式
经过这节内容的讲解,如果让你选择一个注册中心( Etcd、Consul、Zookeeper、Nacos、Eureka 等),你会选哪个呢?还是你会选择自研呢?
上一篇:学习Service Mesh知识点及正确的学习姿势
下一篇:Service Mesh中的服务注册和发现如何实现