为什么要使用服务发现功能?

 

当调用REST API 或Thrift API的服务时,我们在构建请求时通常需要知道服务实例的IP和端口。在传统应用中,服务实例的地址信息相对固定,可以从配置文件中读取。而这些地址也只是只会偶尔更新。

但在现代应用程序中,往往是基于云的微服务架构,此时获取服务实例的IP和端口便是一个需要解决的难题。如下图所示:

 

要学习微服务的服务发现?先来了解一些科普知识吧_java服务实例实例的IP是动态分配。同时,还面临着服务的增减、故障以及升级等变化。这对于客户端程序来说,就需要使用更精确的服务发现机制。

目前,服务发现模式主要有两种:客户端发现模式和服务端发现模式。先来看一下客户端发现模式。

 

客户端发现模式

 

使用客户端发现模式时,客户端负责判断服务实例的可用性和请求的负载均衡。服务实例存储在注册表中,也就是说注册表是服务实例的数据库。客户端通过查询服务注册表,获得服务实例列表,然后使用负载均衡算法从中选择一个,然后发起请求。

下图为这种模式的架构图:

要学习微服务的服务发现?先来了解一些科普知识吧_java_02

此种模式下,当服务实例启动时,会将自己的地址信息注册到服务注册表,当服务停止时从服务注册表中移除。这期间,通常使用心跳机制来定刷新服务实例的注册。

Netflix Eureka就是一个服务注册表组件,它提供了基于REST API的服务实例注册和查询功能。Netflix Ribbon是一个IPC客户端,可配合Eureka实现对服务实例请求的负载均衡。

客户端发现模式的优点是相对简单,除服务注册表外,不需要其他部分做改动。同时,由于客户端知道所有的可用实例,可以做出更明智的、基于特定应用场景的负载均衡决策,比如使用一致性哈希算法。这种模式的缺点是将客户端和服务注册表功能耦合在了一起,必须为每种编程语言和框架的客户端实现服务发现逻辑。

 

服务器端发现模式

另外一种服务发现模式就是服务器发现模式。下图中展示了该模式的结构:

要学习微服务的服务发现?先来了解一些科普知识吧_java_03

客户端通过负载均衡器向服务发起请求,负载均衡器查询服务注册表,并将请求路由到可用的服务实例。与客户端发现相比,服务实例是通过服务注册表进行注册和注销的。

AWS的ELB(Elastic Load Balancer)就是服务器端发现路由器的示例。ELB通常用于负载均衡来自外网的流量,但你也可以使用ELB来负载均衡私有云(VPV)内部的流量。客户端使用DNS名称,通过ELB发送请求(Http或TCP),ELB在已注册的弹性计算云(EC2)实例或EC2容器服务(ECS)的容器之间进行负载均衡。这种实现并没有单独的服务注册表,而是将EC2实例和ECS容器注册到ELB自身上。

Http服务器和负载均衡器(比如,Nginx plus和Nginx)也可以用作服务器端发现的负载均衡器。比如,使用Consul模板动态配置Nginx反向代理。Consul可以从存储在Consul服务注册表中的配置数据中定时重新生成任意配置文件。每当文件改变时,可以运行一个任意shell命令。比如,Consul模板生成一个nginx.conf文件,用于配置反向代理,然后执行命令告诉Nginx去重新加载配置。

某些部署环境(例如Kubernetes和Marathon)会在集群中的每个主机上运行一个代理。这个代理扮演服务器端发现负载平衡器的角色。客户端向服务发出请求时,会通过代理进行路由,透明地将请求转发到集群中某个服务实例。

服务器端发现模式最大的优点是,服务发现的实现细节从客户端抽离出来了,客户端只用发送请求到负载均衡器即可。这样就无需为每种编程语言和框架的客户端实现服务发现逻辑。而且,某些部署环境已经免费提供了该功能。当然,这种模式也有一些缺点,如果部署环境未提供负载均衡器,你还需要搭建和管理一个额外的高可用系统组件。

 

服务注册表

 

服务注册表是服务发现的关键,它是一个包含服务实例地址信息的数据库。服务注册表需要具有高可用性和实时更新性。客户端可以缓存从注册表获得的服务实例地址信息。但这些信息会过时,因此,服务注册表也需要是集群模式,且集群之间还需要通过协议维持一致性。

Netflix Eureka是一个服务注册表组件,它提供了基于REST API形式的服务实例注册和查询功能。一个服务实例可以通过POST请求将自己注册到注册表中;可以通过PUT请求,每隔30秒刷新它的注册信息;可以通过Http的DELETE请求或超时机制来删除实例的注册信息;可以使用Http的GET请求来检索注册的服务实例。

常见的服务注册表组件有:etcd、Consul、Apache Zookeeper、Nacos等。

 

服务注册的选项

 

服务实例必须通过注册表进行注册或注销,通常有几种不同方式来处理注册和注销。一种是服务实例自己注册,即自我注册模式;另一种是基于其他系统组件来管理服务实例的注册,即第三方注册模式。先来看一下自我注册模式。

 

自我注册模式

 

当使用自我注册模式时,服务实例负责在服务注册表中进行自身的注册和注销。如果需要,服务实例还需要发送心跳请求以避免因超时而被注销。下图展示了这种模式的结构图:

要学习微服务的服务发现?先来了解一些科普知识吧_java_04

Netflix OSS Eureka客户端就是这种模式的示例,Eureka客户端负责处理服务实例所有的注册和注销事项。在Spring Cloud项目中,实现了包括服务发现的各种模式,基于此可以很轻松的实现自动注册服务实例到Eureka。你只需在Java配置类上使用@EnableEurekaClient注解即可。

 

自我注册模式的优点是使用起来非常简单,不需要任何其他系统组件。缺点是服务实例与服务注册表紧密耦合,需要在每种编程语言和框架中实现注册功能。

 

另外一种方式可以让服务和注册表解耦的方式就是第三方注册模式。

 

第三方注册模式

 

当使用第三方注册模式时,服务实例不再负责将自己注册到服务注册表。这一功能由第三方组件作为服务注册商来处理。服务注册商通过轮询或订阅事件来跟踪实例的变化。当发现新的可用服务实例时,会将服务实例注册到服务注册表中。同时,也会注销已经停止的服务实例。

 

下图展示了这种模式的结构:

要学习微服务的服务发现?先来了解一些科普知识吧_java_05

开源项目Registrator便是一个示例,它可以基于Docker容器自动注册和注销服务实例。Registrator支持多种注册表,包括etcd和Consul。

 

NetflixOSS Prana项目是另外一个示例,它主要用于非JVM语言编写的服务,是与服务实例并行的Sidecar应用程序。Prana基于Netflix Eureka注册和注销服务实例。

 

第三方注册模式的优点是服务与服务注册表分离,无需每种编程语言和框架的客户端实现服务注册逻辑,而是在专用服务内以集中方式处理服务实例注册。这种模式缺点是,除非部署环境提供内置服务,否则还需要额外搭建和管理一个高度可用的系统组件。

 

总结

 

在微服务应用程序中,服务实例运行状态会动态更改,实例会动态分配地址。因此,为了使客户端可以正常请求服务,必须使用服务发现机制。而本文正是围绕服务发现中的两种模式(客户端发现和服务器端发现)、服务注册表及其两种途径(自我注册模式和第三方注册模式)、反向代理服务器等知识点进行讲解。只有科普了以上基础知识,我们才能更好的学习和认识微服务中的服务发现功能。