阿里架构师讲面试:服务管理之服务注册与发现

529 阅读9分钟

作者简介

2012年本科毕业,2016年硕士毕业。曾供职于IBM中国研发中心,国企,蚂蚁金服等多家企业。接触java开发10余年,目前专注于分布式应用架构师相关知识系统化总结和分享。希望对需要的朋友们系统化得学习和积累相关领域有所帮助。

写作不易,觉得不错的同学顺手点个赞吧,通透的知识让更多需要的朋友看到!

服务管理

分布式应用架构体系对于业务逻辑复用的需求十分强烈,上层业务都想借用已有的底层服务,来快速搭建更多更丰富的应用降低新业务开展的人力和时间成本,快速满足瞬息万变的市场需求。公共的业务被拆分出来,形成可共用的服务,最大程度地保障了代码和逻辑的复用,避免重复建设,这种设计也被成为SOA(service-Oriented Architecture)

服务拆分、服务调用、服务发现,服务依赖,服务的关键度定义…… 服务治理的最大意义是需要把服务拆分(服务子域边界划分)、服务间的依赖关系、服务调用链,以及关键的服务给梳理出来,并对这些服务进行性能和可用性方面的管理。

服务注册与发现

消费者初始化一次性拉取引用服务节点列表;

发布者节点发生变更后,注册中心更新,主动推到服务消费者节点。消费者定时到注册中心拉取最新列表。

服务注册

注册的 IP 和端口怎么确定 ?

IP确定:通过遍历网卡的方式去获取,找到第一个不为本地环回地址的 IP 地址。绝大多数情况下,这个方式比较好用,dubbo 等框架采用的就是这种方法。

端口:如果是 RPC 应用,启动的时候都有一个配置来指定服务监听的端口, 注册的时候直接使用配置项的端口值。

实现服务治理还需要注册哪些信息 ?

简单地将 IP 和 port 信息注册上去,可以满足基本的服务调用的需求,但是在业务发展到一定程度的时候,我们还会有这些需求:

  • 对相同服务下的不同节点设置不同的权重,进行流量调度。

  • 将服务分成预发环境和生产环境,方便进行AB Test功能。

  • 不同机房的服务注册时加上机房的标签,以实现同机房优先的路由规则。

这些高级功能的实现,本质上是依赖于客户端调用时候的负载均衡策略和调用策略,但是如果服务元数据没有注册上来,也只能是巧妇难为无米之炊。一个良好的服务注册中心在设计最初就应该支持这些扩展字段。

如何进行优雅的服务注册与服务下线 ?

优雅发布

虽然服务注册一般发生在服务的启动阶段,但是细分的话,服务注册应该在服务已经完全启动成功,并准备对外提供服务之后才能进行注册。

  • 有些 RPC 框架自身提供了方法来判断服务是否已经启动完成,如 Thrift ,我们可以通过 Server.isServing() 来判断。

优雅下线

绝大多数的服务注册中心都提供了健康检查功能,在应用停止后会自动摘除服务所对应的节点。但是我们也不能完全依赖此功能,应用应该在停止时主动调用服务注册中心的服务下线接口。服务发现

  • 更优雅的方式,先将即将停止的应用服务器所对应的权重调成 0,此时上游将不再调用此服务器。这时候的停止应用的操作对服务订阅者完全没有影响,当然这种场景需要订阅者实现按权重的负载均衡和运维部署工具深度结合。

服务的健康检查是如何做的 ?

健康检查分为客户端心跳和服务端主动探测两种方式。

  • 客户端心跳。

  • 客户端每隔一定时间主动发送“心跳”的方式来向服务端表明自己的服务状态正常,心跳可以是 TCP 的形式,也可以是 HTTP 的形式。

  • 也可以通过维持客户端和服务端的一个 socket 长连接自己实现一个客户端心跳的方式。

但是客户端心跳中,长连接的维持和客户端的主动心跳都只是表明链路上的正常,不一定是服务状态正常。

服务端主动调用服务进行健康检查是一个较为准确的方式,返回结果成功表明服务状态确实正常。

  • 服务端主动探测

  • 对于没有提供 HTTP 服务的 RPC 应用,服务端调用服务发布者的接口来完成健康检查。

  • 可以通过执行某个脚本的形式来进行综合检查。

服务端主动探测也存在问题。服务注册中心主动调用 RPC 服务的某个接口无法做到通用性;在很多场景下服务注册中心到服务发布者的网络是不通的,服务端无法主动发起健康检查。

所以如何取舍,还是需要根据实际情况来决定,根据不同的场景,选择不同的策略。

服务发现

怎么找到服务注册中心的地址?

  • 在应用的配置文件中指定服务注册中心的地址,类似于 zookeeper 和 eureka。
  • 指定一个地址服务器的地址,然后通过这个地址服务器来获取服务注册中心的地址,地址服务器返回的结果会随着服务注册中心的扩缩容及时更新。

当服务有节点退出或新的节点加入时,订阅者如何及时收到通知 ?

很经典的 Push 和 Pull 问题。

Push 的经典实现有两种,基于 socket 长连接的 notify,典型的实现如 zookeeper;另一种为 HTTP 连接所使用 Long Polling。

但是基于 socket 长连接的 notify 和基于 HTTP 协议的 Long Polling 都会存在notify消息丢失的问题。

所以通过 Pull 的方式定时轮询也必不可少,时间间隔的选择也很关键,频率越高服务注册中心所承受的压力也越大。需要结合服务端的性能和业务的规模进行权衡。

我能方便地查看我发布和订阅了哪些服务,订阅的服务有哪些节点吗 ?

  • 一个好的产品,用户使用体验和运维体验必须是优雅的,如果查看本机发布和订阅的服务,只能通过查看日志,甚至是 jmap 的方式来获取,显然体验非常糟糕。

  • 服务注册中心应该提供了丰富的接口,支持根据应用名、IP、订阅服务名、发布服务名,来进行多层次的组合查询。

  • 同时,客户端的内存里,同样也应该保留服务发布与订阅的各种信息,并提供方式供人方便地查询。

高可用和容灾

服务注册中心性能

当服务节点数越来越多时,服务注册中心的性能会成为瓶颈,这时候就需要通过水平扩容来提升服务注册中心集群的性能。

  • 对于那些采用了类 Paxos 协议的强一致性的组件,如ZooKeeper,由于每次写操作需要过半的节点确认。水平扩容不能提升整个集群的写性能,只能提升整个集群的读性能。
  • 而对于采用最终一致性的组件来说,水平扩容可以同时提升整个集群的写性能和读性能。

客户端容灾策略

  1. 首先,本地内存缓存,当运行时与服务注册中心的连接丢失或服务注册中心完全宕机,仍能正常地调用服务。

  2. 然后,本地缓存文件,当应用与服务注册中心发生网络分区或服务注册中心完全宕机后,应用进行了重启操作,内存里没有数据,此时应用可以通过读取本地缓存文件的数据来获取到最后一次订阅到的内容。

  3. 最后,本地容灾文件夹。正常的情况下,容灾文件夹内是没有内容的。当服务端完全宕机且长时间不能恢复,同时服务提供者又发生了很大的变更时,可以通过在容灾文件夹内添加文件的方式来开启本地容灾。此时客户端会忽略原有的本地缓存文件,只从本地容灾文件中读取配置。

注册中心端容灾策略

  • 当有新节点加入集群时,节点启动后能自动添加到地址服务器中,并通过地址服务器找到其他节点,自动从其他节点同步数据,以达到数据的最终一致性。
  • 当某个节点宕机时,此服务注册中心节点的信息会自动地址服务器中摘除,客户端能及时感知到此节点已下线。

服务端的无状态性保证了服务的容灾和高可用可以做的很薄。

业务安全如何实现?

在业务安全方面,应该在每一次的发布、订阅、心跳,都带上鉴权的信息就行验签和鉴权,确保业务信息的安全性。

觉得有收获的话帮忙点个赞吧,让有用的知识分享给更多的人

## 欢迎关注掘金号:五点半社

## 关注微信公众号:五点半社(工薪族的财商启蒙)##