极客时间已完结课程限时免费阅读

05 | 如何注册和发现服务?

05 | 如何注册和发现服务?-极客时间

05 | 如何注册和发现服务?

讲述:胡忠想

时长09:01大小4.14M

专栏上一期,我给你介绍了服务发布和引用常用的三种方式:RESTful API、XML 配置以及 IDL 文件。假设你已经使用其中一种方式发布了一个服务,并且已经在一台机器上部署了服务,那我想问你个问题,如果我想调用这个服务,我该如何知道你部署的这台机器的地址呢?
这个问题就跟我想去吃肯德基一样,我可以去谷歌地图上搜索肯德基,然后谷歌地图会返回所有的肯德基店面的地址,于是我选择距离最近的一家去吃。这里面谷歌地图就扮演了一个类似注册中心的角色,收录了所有肯德基店面的地址。
同理,我想知道这台服务器的地址,那是不是可以去一个类似“谷歌地图”的地方去查呢?是的,在分布式系统里,就有一个类似的概念,不过它的名字可不是叫什么地图,而是叫注册中心。但原理和地图其实差不多,就是将部署服务的机器地址记录到注册中心,服务消费者在有需求的时候,只需要查询注册中心,输入提供的服务名,就可以得到地址,从而发起调用。
下面我来给你详细讲解下注册中心的原理和实现方式

注册中心原理

在微服务架构下,主要有三种角色:服务提供者(RPC Server)、服务消费者(RPC Client)和服务注册中心(Registry),三者的交互关系请看下面这张图,我来简单解释一下。
RPC Server 提供服务,在启动时,根据服务发布文件 server.xml 中的配置的信息,向 Registry 注册自身服务,并向 Registry 定期发送心跳汇报存活状态。
RPC Client 调用服务,在启动时,根据服务引用文件 client.xml 中配置的信息,向 Registry 订阅服务,把 Registry 返回的服务节点列表缓存在本地内存中,并与 RPC Sever 建立连接。
当 RPC Server 节点发生变更时,Registry 会同步变更,RPC Client 感知后会刷新本地内存中缓存的服务节点列表。
RPC Client 从本地缓存的服务节点列表中,基于负载均衡算法选择一台 RPC Sever 发起调用。

注册中心实现方式

注册中心的实现主要涉及几个问题:注册中心需要提供哪些接口,该如何部署;如何存储服务信息;如何监控服务提供者节点的存活;如果服务提供者节点有变化如何通知服务消费者,以及如何控制注册中心的访问权限。下面我来一一给你讲解。
1. 注册中心 API
根据注册中心原理的描述,注册中心必须提供以下最基本的 API,例如:
服务注册接口:服务提供者通过调用服务注册接口来完成服务注册。
服务反注册接口:服务提供者通过调用服务反注册接口来完成服务注销。
心跳汇报接口:服务提供者通过调用心跳汇报接口完成节点存活状态上报。
服务订阅接口:服务消费者通过调用服务订阅接口完成服务订阅,获取可用的服务提供者节点列表。
服务变更查询接口:服务消费者通过调用服务变更查询接口,获取最新的可用服务节点列表。
除此之外,为了便于管理,注册中心还必须提供一些后台管理的 API,例如:
服务查询接口:查询注册中心当前注册了哪些服务信息。
服务修改接口:修改注册中心中某一服务的信息。
2. 集群部署
注册中心作为服务提供者和服务消费者之间沟通的桥梁,它的重要性不言而喻。所以注册中心一般都是采用集群部署来保证高可用性,并通过分布式一致性协议来确保集群中不同节点之间的数据保持一致。
以开源注册中心 ZooKeeper 为例,ZooKeeper 集群中包含多个节点,服务提供者和服务消费者可以同任意一个节点通信,因为它们的数据一定是相同的,这是为什么呢?这就要从 ZooKeeper 的工作原理说起:
每个 Server 在内存中存储了一份数据,Client 的读请求可以请求任意一个 Server。
ZooKeeper 启动时,将从实例中选举一个 leader(Paxos 协议)。
Leader 负责处理数据更新等操作(ZAB 协议)。
一个更新操作成功,当且仅当大多数 Server 在内存中成功修改 。
通过上面这种方式,ZooKeeper 保证了高可用性以及数据一致性。
3. 目录存储
还是以 ZooKeeper 为例,注册中心存储服务信息一般采用层次化的目录结构:
每个目录在 ZooKeeper 中叫作 znode,并且其有一个唯一的路径标识。
znode 可以包含数据和子 znode。
znode 中的数据可以有多个版本,比如某一个 znode 下存有多个数据版本,那么查询这个路径下的数据需带上版本信息。
4. 服务健康状态检测
注册中心除了要支持最基本的服务注册和服务订阅功能以外,还必须具备对服务提供者节点的健康状态检测功能,这样才能保证注册中心里保存的服务节点都是可用的。
还是以 ZooKeeper 为例,它是基于 ZooKeeper 客户端和服务端的长连接和会话超时控制机制,来实现服务健康状态检测的。
在 ZooKeeper 中,客户端和服务端建立连接后,会话也随之建立,并生成一个全局唯一的 Session ID。服务端和客户端维持的是一个长连接,在 SESSION_TIMEOUT 周期内,服务端会检测与客户端的链路是否正常,具体方式是通过客户端定时向服务端发送心跳消息(ping 消息),服务器重置下次 SESSION_TIMEOUT 时间。如果超过 SESSION_TIMEOUT 后服务端都没有收到客户端的心跳消息,则服务端认为这个 Session 就已经结束了,ZooKeeper 就会认为这个服务节点已经不可用,将会从注册中心中删除其信息。
5. 服务状态变更通知
一旦注册中心探测到有服务提供者节点新加入或者被剔除,就必须立刻通知所有订阅该服务的服务消费者,刷新本地缓存的服务节点信息,确保服务调用不会请求不可用的服务提供者节点。
继续以 ZooKeeper 为例,基于 ZooKeeper 的 Watcher 机制,来实现服务状态变更通知给服务消费者的。服务消费者在调用 ZooKeeper 的 getData 方法订阅服务时,还可以通过监听器 Watcher 的 process 方法获取服务的变更,然后调用 getData 方法来获取变更后的数据,刷新本地缓存的服务节点信息。
6. 白名单机制
在实际的微服务测试和部署时,通常包含多套环境,比如生产环境一套、测试环境一套。开发在进行业务自测、测试在进行回归测试时,一般都是用测试环境,部署的 RPC Server 节点注册到测试的注册中心集群。但经常会出现开发或者测试在部署时,错误的把测试环境下的服务节点注册到了线上注册中心集群,这样的话线上流量就会调用到测试环境下的 RPC Server 节点,可能会造成意想不到的后果。
为了防止这种情况发生,注册中心需要提供一个保护机制,你可以把注册中心想象成一个带有门禁的房间,只有拥有门禁卡的 RPC Server 才能进入。在实际应用中,注册中心可以提供一个白名单机制,只有添加到注册中心白名单内的 RPC Server,才能够调用注册中心的注册接口,这样的话可以避免测试环境中的节点意外跑到线上环境中去。

总结

注册中心可以说是实现服务化的关键,因为服务化之后,服务提供者和服务消费者不在同一个进程中运行,实现了解耦,这就需要一个纽带去连接服务提供者和服务消费者,而注册中心就正好承担了这一角色。此外,服务提供者可以任意伸缩即增加节点或者减少节点,通过服务健康状态检测,注册中心可以保持最新的服务节点信息,并将变化通知给订阅服务的服务消费者。
注册中心一般采用分布式集群部署,来保证高可用性,并且为了实现异地多活,有的注册中心还采用多 IDC 部署,这就对数据一致性产生了很高的要求,这些都是注册中心在实现时必须要解决的问题。

思考题

最后请你思考一下,你觉得采用注册中心来实现服务发现与传统的 DNS 实现服务发现有什么不同吗?
欢迎你在留言区写下自己的思考,与我一起讨论。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 16

提建议

上一篇
04 | 如何发布和引用服务?
下一篇
06 | 如何实现RPC远程服务调用?
unpreview
 写留言

精选留言(51)

  • oddrock
    2018-09-01
    1. 注册中心的服务注册和发现都是基于API的,可以自动化注册与发现,dns则是人工注册。这样也导致前者实时性、容错性好于后者。 2. 注册中心可以注册http、rpc等各种服务,dns只能注册http服务 3. 注册中心对已注册的服务会有主动的、自动化的健康检查机制,dns没有。 4. 注册中心一般是一级分布式的,dns则是多级架构,例如根域名服务器、权威域名服务器等 5. 注册中心的安全机制相对dns弱一些,毕竟是内部使用。
    展开

    作者回复: 总结的不错

    147
  • snakorse
    2018-09-01
    不同意某楼的观念,dns同样也可以用于非http服务的,我觉得dns相较于zk这样的注册中心,劣势在于1. dns的维护管理比较麻烦,甚至需要手工配置 2.dns更新后生效有延迟 3. 客户端一般只能同时连接到一个server ip,无法做请求的负载均衡,而注册中心的方式通常客户端会与所有server建立连接形成连接池,从而在调用端实现请求的负载均衡

    作者回复: 理解很深刻👍

    89
  • null
    2018-09-29
    问题一: 注册中心原理提到:向 Registry 订阅服务,把 Registry 返回的服务节点列表缓存在本地内存中,并与 RPC Server 建立连接。 疑问: 请问老师,client 与 server 这时候就建立连接了么?不是需要调用服务时才真正建立连接么? -------- 问题二: 服务提供者和服务消费者可以同任意一个 zk 节点通信,因为集群中 zk 的数据一定是相同的,这是为什么呢?这就要从 zk 的工作原理说起:Leader 负责处理数据更新等操作;一个更新操作成功,当且仅当大多数 zk server 在内存中成功修改。 疑问: 大多数 zk server 内存数据修改成功,则认为此次更新操作是成功的。但是 zk client 请求到未更新数据的 zk server,这时仍是旧数据,怎么说明“集群中 zk 的数据一定是相同的”呢?
    展开
    共 1 条评论
    22
  • 2018-09-01
    注册中心采用consul,注册通过docker registrator自动注册反注册、健康检查,服务发现采用consul_template nginx的grpc代理,业务代码不需要关注注册中心,这种模式业务耦合行就非常小了,只需要给业务知晓nginx即可。

    作者回复: 专栏有一讲开源注册中心选型里会提到你说的这种应用外注册的方式,特别是适合容器化的应用,业务不需要引入注册中心SDK

    21
  • LinkMaq
    2018-11-06
    其实dns最大的劣势是不能实现端口级别的服务发现

    作者回复: 是,只能做到ip级别

    共 2 条评论
    14
  • 包子
    2018-09-12
    其实我想说,zk并不合适做注册中心,注册中心应该是一个AP的系统

    作者回复: 对 专栏后面会详细分析

    共 2 条评论
    9
  • 2018-09-19
    一个更新操作成功,当且仅当大多数 Server 在内存中成功修改。 没看懂这句是什么意思
    共 2 条评论
    5
  • Yangjing
    2018-09-02
    DNS 不会主动将变更的服务信息通知服务消费者客户端; 服务消费者查询服务的方式也不一样
    5
  • 爪哇夜未眠
    2018-09-02
    老师好,请问zookeeper和eureka作为注册中心的区别呢

    作者回复: 专栏后面有一节会讲,简单说下zk注重强一致性,在网络分区的情况下就不可用了,而eureka注重可用性,即使网络分区了,数据有短暂不一致,也要可用

    共 2 条评论
    5
  • 爬行的蜗牛
    2020-05-02
    1. 注册中心的作用 类似地图的中心, 把为负心的信息(服务名,地址等)保存管理起来, 服务消费者通过注册中心找到对应的服务。 2. 注册中心的角色 1) 服务提供者(RPC Sever) 2) 服务消费则 (RPC Client) 3) 服务注册中心(Registry) 服务提供者: 服务提供者向注册中心注册自身服务,并定期发送注册中心发送存活性的心跳。 服务消费则: 向注册中心订阅服务, 把注册中心返回的服务结点列表缓存在本地内存中,并与服务提供者(Server)通信。 其他: 当服务提供者结点发生变更的时候, Registry 会同步变更,Client服务消费者 感知后会刷新本地内存中国呢缓存的服务结点列表。 服务消费者会从本地缓存的服务结点列表中, 基于负载均衡的算法选择一台Server 服务提供者来调用。 3. 注册中心的实现方式 3.1 注册中心API 服务注册接口: 服务提供者通过调用服务注册接口来完成服务注册 服务反注册接口: 服务提供者通过调用服务反注册接口来完成服务注销。 心跳汇报接口 服务订阅接口: 服务订阅者通过调用服务变更查询接口, 获取最新的可用服务结点列表。 服务变更查询接口: 服务消费者通过调用服务变更查询接口, 获取最新的可用服务节点列表。 服务查询接口: 查询注册中心当前注册了哪些服务 服务修改接口: 修改注册中心某一服务的信息 3.2 集群部署 以开源注册中心zookeeper 为例: zookeeper 集群, 集群中有多台server, 从server 中选举出一个Leader, leader 负责处理数据更新等重要操作, 一个更新操作,大多数server 在内存中成功修改, 则成功。每台server 都保存了完整的注册中心信息。 3.3 目录存储 每个目录在zookeeper 中叫做 znode, 并且是唯一的路径标识。 znode 可以包含数据和子node. 3.4 服务状态监控检查 3.5 服务状态变更通知 3.6 白名单机制 只有添加到注册中心白名单的服务提供者(RPC Server )才能进入。 总结 注册中心是实现服务化的关键, 实现服务化之后, 服务提供者和消费者不在同一个进程中运行, 实现了解藕, 注册中心实现了服务提供者和消费者之间的纽带。
    展开
    3
  • 2019-05-21
    微服务铁三角:提供者、消费者、纽带-注册中心。注册中心起到链接的作用,如果链接上了,没有了纽带也是OK的,纽带有些像中介,尤其像租房中介。 话说链家是有头的,为了数据的一致性有人专门负责修改信息。 并且一个城市里的租房信息,在这个城市里的店员都是可以访问的。 当提供者和消费者比较多,且流动性强变化频繁是非常需要中介的存在的,否则房子租出去的周期就会长许多啦!
    展开
    3
  • ward-wolf
    2018-11-15
    1.注册中心具有检查功能,可以计算剔除不可用的服务,传统dns没有这个功能 2.传统dns服务对外暴露的一般是不变的,服务节点如果宕机。客户端不能及时感知 3.dns需要人工维护,注册中心可以实现自动注册
    3
  • 技术修行者
    2018-09-01
    可以列举一下业界常用的服务发现组件吗?它们之间有什么优劣?

    作者回复: 后面有一节专门讲zk、eureka、consul

    共 3 条评论
    3
  • dancer
    2018-09-01
    DNS发布变更慢,数据一致性较差;DNS基于域名解析,注册中心基于服务名进行解析;注册中心是自己开发维护的,方便功能迭代以及与其他组件进行扩展。
    3
  • 2019-03-29
    服务注册机制主要可以解决多机多实例部署发现,在一台机器部署同一服务多个实例,每个实例使用不同端口。 dns机制只能适合多机,每个机器使用固定端口方式
    2
  • 花生
    2018-12-12
    如果网络不稳,服务提供者频繁掉线,重新上线,注册中心频繁向服务消费者通知服务列表变更,这样会否导致系统崩溃?

    作者回复: 注册中心如何防止网络抖动引起的问题,专栏后面有讲

    2
  • hekaiEscape
    2018-09-04
    消费者是怎么知道服务变更的,是通过与注册中心建立长链接,然后监听吗?
    共 1 条评论
    2
  • 蓝猫
    2020-05-25
    微服务架构中基于DNS的服务发现:https://yq.aliyun.com/articles/598792
    1
  • 陈国林
    2020-02-26
    1. DNS能够提供的一个稳定的域名,对于服务的消费者来说只需要根据域名进行请求就可以非常方便,天然的分布式,而比注册中心是需要自行维护的组件 2. DNS是没有办法实现监听机制的,这就意味着服务消费者只能自己不断的轮询获取最新的服务地址。 3. DNS会有各种中间链路缓存存在,导致服务提供者更新之后,服务消费者还有可能请求到老的服务或者不存在的服务
    展开
    1
  • 北风之神
    2018-12-16
    您好, 感觉本章和上一章的名字容易混淆,发布和引用,注册和发现,总结一下, 发布就是服务提供者告诉调用者服务的内容,比如请求参数、返回字段等,引用是服务调用者指明需要调用的服务的内容,比如请求参数、返回字段等;注册是服务提供者告诉调用者它在哪里,比如它IP端口等,发现是服务调用者找到服务提供者。
    1