1. Eureka服务注册与发现
一套微服务架构的系统由很多单一职责的服务单元组成,而每个服务单元又有众多运行实例。例如,世界上最大的收费视频网站Netflix的系统是由600多个服务单元构成的,运行实例的数量就更加庞大了。由于各服务单元颗粒度较小、数量众多,相互之间呈现网状依赖关系,因此需要服务注册中心来统一管理微服务实例,维护各服务实例的健康状态。
2. 什么是服务注册与发现
- 从宏观角度,微服务架构下的系统角色可以简单分为注册中心、服务提供者、远程客户端组件。
2.服务注册是指服务提供者将自己的服务信息(如服务名、IP地址等)告知服务注册中心。
3.服务发现:注册中心客户端组件从注册中心查询所有服务提供者信息,当其他服务下线后,注册中心能够告知注册中心客户端组件这种变化。
4.远程客户端组件与服务提供者之间一般使用某种RPC通信机制来进行服务消费,常见的RPC通信方式为REST API,底层为HTTP传输协议。服务提供者通常以Web服务的方式提供REST API接口;远程客户端组件则通常以模块组件的方式完成RESTAPI的远程调用。
5.注册中心的主要功能如下:
1.服务注册表维护:此功能是注册中心的核心,用来记录各个服务提供者实例的状态信息。注册中心提供Provider实例清单的查询和管理API,用于查询可用的Provider实例列表,管理2.Provider实例的上线和下线。
服务健康检查:注册中心使用一定机制定时检测已注册的Provider实例,如发现某实例长时间无法访问,就会从服务注册表中移除该实例。
6.服务提供者的主要功能如下:
1.服务注册:是指Provider微服务实例在启动时(或者定期)将自己的信息注册到注册中心的过程。
2.心跳续约:Provider实例会定时向注册中心提供“心跳”,以表明自己还处于可用的状态。当一个Provider实例停止心跳一段时间后,注册中心会认为该服务实例不可用了,就会将该服务实例从服务注册表中剔除。如果被剔除掉的Provider实例过了一段时间后又继续向注册中心提供心跳,那么注册中心会把该Provider实例重新加入服务注册表中。
3.健康状况查询:Provider实例能提供健康状况查看的API,注册中心或者其他的微服务Provider能够获取其健康状况。
7.服务提供者的服务注册和心跳续约一般都会通过注册中心客户端组件来完成。注册中心客户端组件还有如下功能:
1.服务发现:从注册中心查询可用Provider实例清单。
2.实例缓存:将从注册中心查询的Provider实例清单缓存到本地,不需要在每次使用时都去注册中心临时获取。
8.Spring Cloud生态体系中存在多种注册中心框架,例如Eureka、Nacos、Consul、ZooKeeper等。
3. Eureka Server注册中心
1.Eureka在业界的应用十分广泛(尤其是国外),整个框架经受住了Netflix严酷生产环境的考验。
2.除了Eureka注册中心外,Netflix的其他服务治理功能也十分强大,包括Ribbon、Hystrix、Feign、Zuul等组件结合到一起组成了一套完整的服务治理框架,使服务的调用、路由变得异常容易。
3.Spring Cloud Eureka是Spring Cloud Netflix微服务套件的一部分,基于Netflix Eureka做了二次封装,主要负责完成微服务实例的自动注册与发现,这也是微服务架构中的核心和基础功能。
4.Eureka所治理的每一个微服务实例被称为Provider Instance(提供者实例)。每一个Provider Instance包含一个Eureka Client组件(相当于注册中心客户端组件),它的主要工作如下:
向Eureka Server完成Provider Instance的注册、续约和下线等操作,主要的注册信息包括服务名、机器IP、端口号、域名等。
向Eureka Server获取Provider Instance清单,并且缓存在本地。
5.实际上一个Eureka Server实例身兼三个角色:注册中心、服务提供者、注册中心客户端组件。主要原因如下:
1.对于所有Provider Instance而言,Eureka Server的角色是注册中心。
2.对于Eureka Server集群中其他的Eureka Server而言,Eureka Server的角色是注册中心客户端组件。
3.Eureka Server对外提供REST接口的服务,当然也是服务提供者。
6.Eureka Server作为注册中心的配置项以eureka.server.*作为前缀:
- eureka.server.enable-self-preservation:此配置项用于设置是否关闭注册中心的保护机制,默认true;
- eureka.server.eviction-interval-timer-in-ms:配置Eureka Server清理无效节点的时间间隔,默认为60 000毫秒(60秒)。但是,如果Eureka Server处于保护状态,此配置就无效。
7.Eureka Server作为服务提供者的配置项以eureka.instance.*作为前缀:
- eureka.instance.hostname:设置当前实例的主机名称。
- eureka.instance.appname:设置当前实例的服务名称。默认值取自spring.application.name配置项的值,如果该选项没有值,eureka.instance.appname的值就为unknown。在Eureka服务器上,服务提供者的名称不区分字母大小写。
- eureka.instance.ip-address:设置当前实例的IP地址。
- eureka.instance.prefer-ip-address:如果配置为true,就使用IP地址的形式来定义Provider实例的访问地址,而不是使用主机名来定义Provider实例的地址。如果同时设置了eureka.instance.ip-address选项,就使用该选项所配置的IP,否则自动获取网卡的IP地址作为Provider实例的访问地址。默认情况下,此配置项的值为false,即使用主机名来定义Provider实例的访问地址。
- eureka.instance.lease-renewal-interval-in-seconds:定义Provider实例到注册中心续约(心跳)的时间间隔,单位为秒,默认值为30秒。
- eureka.instance.lease-expiration-duration-in-seconds:定义Provider实例失效的时间,单位为秒,默认值为90秒。
- eureka.instance.status-page-url-path:定义Provider实例状态页面的URL,此选项配置的是相对路径,默认使用HTTP访问,如果需要使用HTTPS,就使用绝对路径配置。默认的相对路径为/info。
- eureka.instance.status-page-url:定义Provider实例状态页面的URL,此选项配置的是绝对路径。
- eureka.instance.health-check-url-path:定义Provider实例健康检查页面的URL,此选项配置的是相对路径,默认使用HTTP访问,如果需要使用HTTPS,就使用绝对路径配置。默认的相对路径为/health。
- eureka.instance.health-check-url:定义Provider实例健康检查页面的URL,此选项配置的是绝对路径。
8.Eureka Server作为注册中心客户端组件的配置项以eureka.client.*作为前缀:
- eureka.client.register-with-eureka:作为Eureka Client,eureka.client.register-with-eureka表示是否将自己注册到其他的Eureka Server上,默认为true。当前集群只有一个Eureka Server时,设置成false。
- eureka.client.fetch-registry:作为Eureka Client,是否从Eureka Server获取注册信息,默认为true。如果是一个单点的Eureka Server,不需要同步其他Eureka Server节点数据,则设置为false。
- eureka.client.registery-fetch-interval-seconds:作为Eureka Client,从Eureka Server获取注册信息的间隔时间,单位为秒,默认为30秒。
- eureka.client.eureka-server-connect-timeout-seconds:Eureka Client组件连接到Eureka Server的超时时间,单位为秒,默认值为5。
- eureka.client.eureka-server-read-timeout-seconds:Eureka Client组件读取Eureka Server信息的超时时间,单位为秒,默认值为8。
- eureka.client.eureka-connection-idle-timeout-seconds:Eureka Client组件到Eureka Server连接的空闲超时的时间,单位为秒,默认值为30。
- eureka.client.filter-only-up-instances:从Eureka Server获取Provider实例清单时是否进行过滤,只保留UP状态的实例,默认值为true。
- eureka.client.service-url.defaultZone:作为Eureka Client,需要向远程的Eureka Server自我注册,发现其他的Provider实例。此配置项用于设置EurekaServer的交互地址,在具有注册中心集群的情况下,多个Eureka Server的交互地址之间可以使用英文逗号分隔开。此配置项涉及Spring Cloud中的Region(地域)与Zone(可用区)两个概念,两者都是借鉴AWS(Amazon云)的概念。在非AWS环境下,Region和Zone可以理解为服务器的位置,Region可以理解为服务器所在的地域,Zone可以理解为服务器所处的机房。一个Region可以包含多个Zone。不同的Region的距离很远,一个Region的不同Zone间的距离往往较近,也可能在同一个物理机房内。在网络环境跨地域、跨机房的情况下,Region与Zone都可以在配置文件中进行配置。配置Region与Zone的主要目的是,在网络环境复杂的情况下帮助客户端就近访问需要的Provider实例。负载均衡组件Spring Cloud Ribbon的默认策略是优先访问与客户端处于同一个Zone中的服务端实例,只有当同一个Zone中没有可用服务端实例时,才会访问其他Zone中的实例。
- eureka.client.serviceUrl.*:此配置项是上面第8项的上一级配置项,用于在多个Zone的场景下配置注册中心,它的类型为HashMap,Key为Zone,Value为机房中的所有注册中心地址。如果没有多个Zone,那么此配置项有一个默认的可用区,Key为defaultZone。
4. 服务提供者的创建和配置
1.首先一个Provider至少需要两个组件包依赖:Spring Boot Web服务组件和EurekaClient组件;Spring Boot Web服务组件用于提供REST接口服务,Eureka Client组件用于服务注册与发现。
2.前面讲到,Spring Cloud中的一个Provider实例身兼两个角色:服务提供者和注册中心客户端。所以,在Provider的配置文件中包含两类配置:Provider实例角色的相关配置和Eureka Client角色的相关配置。
4.1. Provider实例角色的相关配置
- eureka.instance.instance-id:此项用于配置Provider实例ID。
- eureka.instance.ip-address:设置当前实例的IP地址。${spring.cloud.client.ip-address}是从Spring Cloud依赖包中导入的配置项,存放了客户端的IP地址。
- eureka.instance.prefer-ip-address:如果配置为true,就使用IP地址的形式来定义Provider实例的地址,而不是使用主机名来定义Provider实例的地址。
- eureka.instance.status-page-url-path:定义Provider实例状态页面的URL,此选项配置的是相对路径,默认使用HTTP访问,如果需要使用HTTPS,就使用绝对路径配置。默认的相对路径为/info。
- eureka.instance.health-check-url-path:定义Provider实例健康检查页面的URL,此选项配置的是相对路径,默认使用HTTP访问,如果需要使用HTTPS,就使用绝对路径配置。默认的相对路径为/health。
4.2. Eureka Client组件的相关配置
- eureka.client.register-with-eureka:作为Eureka Client,eureka.client.register-with-eureka表示是否将自己注册到Eureka Server。
- eureka.client.fetch-registry:作为Eureka Client,是否从Eureka Server获取注册信息。
- eureka.client.service-url.defaultZone:作为Eureka Client,需要向远程的Eureka Server自我注册,查询其他的提供者。此配置项用于设置此客户端默认Zone(类似于默认机房)的Eureka Server的交互地址。
5. 服务提供者的续约(心跳)
- 服务提供者的续约(心跳)保活由Provider Instance主动定期执行来实现,每隔一段时间就调用Eureka Server提供的REST保活接口,发送Provider Instance的状态信息给注册中心,告诉注册中心注册者还在正常运行。
- Provider Instance的续约默认是开启的,续约默认的间隔是30秒,也就是每30秒会向Eureka Server发起续约(Renew)操作。
- eureka.instance.lease-renewal-interval-in-seconds:表示ProviderInstance的Eureka Client组件发送续约(心跳)给Eureka Server的时间间隔。
- eureka.instance.lease-expiration-duration-in-seconds:此配置项设置了租约有效期,在租约时间内,如果Eureka Client未续约(心跳),Eureka Server将剔除该服务。
- 租约有效期需要合理设置,如果有效期太长,那么在服务消费客户端访问的时候,该Provider Instance 可能已经宕机了。如果该值设置得太小,那么ProviderInstance很可能因为临时的网络抖动而被Eureka Server剔除掉。
- Eureka Server提供了多个和Provider Instance相关的Spring上下文ApplicationEvent(应用事件)。当Server启动、服务注册、服务下线、服务续约等事件发生时,Eureka Server会发布相对应的ApplicationEvent,以方便应用程序进行监听。
- 下面介绍几个常见的Eureka Server应用事件。
- EurekaInstanceRenewedEvent:服务续约事件。
- EurekaInstanceRegisteredEvent:服务注册事件。
- EurekaInstanceCanceledEvent:服务下线事件。
- EurekaRegistryAvailableEvent:Eureka注册中心启动事件。
- EurekaServerStartedEvent:Eureka Server启动事件。
- 如果需要监听Provider Instance的服务注册、服务下线、服务续约等事件,那么可以在Eureka Server中编写相应的事件监听程序。
6. 服务提供者的健康状态
- Eureka Server并不记录Provider的所有健康状况信息,仅仅维护了一个Provider清单。Eureka Client组件查询的Provider注册清单中,包含每一个Provider的健康状况的检查地址。通过该健康状况的地址可以查询Provider的健康状况。
- 在Eureka Server响应的Provider的详细信息中,有3个与Provider实例的健康状态有关的信息,现分别说明如下:
- status:status是Provider实例本身发布的健康状态。status的值为UP表示应用程序状态正常。除了UP外,应用健康状态还有DOWN、OUT_OF_SERVICE、UNKONWN等其他取值,不过只有状态为UP的Provider实例会被Eureka Client组件请求。
- healthCheckUrl:healthCheckUrl是Provider实例的健康信息URL地址,默认为Spring Boot Actuator组件中ID为health的Endpoint(端点),它的默认URL地址为/actuator/health。
- statusPageUrl:statusPageUrl是Provider实例的状态URL地址,默认为Spring Boot Actuator组件中ID为info的Endpoint(端点),它的默认URL地址为/actuator/info。
7. Eureka自我保护模式与失效Provider的快速剔除
- Provider服务实例注册到Eureka Server后会维护一个心跳连接,告诉EurekaServer自己还活着。Eureka Server在运行期间会统计所有Provider实例的心跳,如果失效比例在一段时间间隔内(如15分钟)低于阈值(如85%),EurekaServer就会将当前所有的Provider实例的注册信息保护起来,让这些实例不会过期。
- 保护模式可能会导致一些问题。有时Provider服务实例会由于内存溢出、网络故障等原因不能正常运行,而处于保护模式的Eureka Server不一定会将其从服务列表中剔除出去,所以会导致客户端出现调用失败。为了使失效的Provider能够快速被剔除,可以停用Eureka Server的保护模式,然后启用客户端的健康状态检查。
- eureka.server.enable-self-preservation=true配置项的默认值为true。也就是说,在默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server就会认为该实例已经出现故障,进而注销该实例(默认为90秒)。当发生网络通信故障时,微服务与Eureka Server之间无法正常通信,以上行为就可能变得非常危险——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过自我保护模式来解决这个问题——当Eureka Server节点在短时间内丢失过多客户端(可能发生了网络故障)时,这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。
- 综上所述,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式可以让Eureka集群更加健壮和稳定。在网络环境好、通信延迟低的场景(如开发环境)中,建议关闭自我保护模式,因为自我保护模式会导致不健康的服务得不到及时的注销。
- 配置项eureka.client.healthcheck.enabled=true应该放在application.yml文件中,而不应该放在bootstrap.yml文件中。如果该选项配置在bootstrap.yml文件中,就可能导致Provider实例在Eureka上的状态为UNKNOWN。