服务注册与服务发现
背景:在传统应用中,组件之间的调用,通过有规范的约束的接口来实现,从而实现不同模块间良好的协作。但是被拆分成微服务后,每个微服务实例的网络地址都可能动态变化,数量也会变化,使得原来硬编码的地址失去了作用。需要一个中心化的组件来进行服务的登记和管理。
概念:实现服务治理,即管理所有的服务信息和状态。
注册中心好处:不用关心有多少提供方。
注册中心有哪些: Eureka,Nacos,Consul,Zookeeper等。
服务注册与发现包括两部分,一个是服务器端,另一个是客户端。
- 服务端(Server)是一个公共服务,为Client提供服务注册和发现的功能,维护注册到自身的Client的相关信息,同时提供接口给Client获取注册表中其他服务的信息,使得动态变化的Client能够进行服务间的相互调用。
- 客户端(Client) 将自己的服务信息通过一定的方式登记到 Server上,并在正常范围内维护自己信息一致性,方便其他服务发现自己,同时可以通过Server获取到自己依赖的其他服务信息,完成服务调用,还内置了负载均衡器,用来进行基本的负载均衡。
服务端和客户端功能
server注册中心功能
- 服务注册表:记录各个微服务信息,例如服务名称,ip,端口等。注册表提供查询 API(查询可用的微服务实例)和管理 API(用于服务的注册和注销)。
- 服务注册与发现:
- 注册:将微服务信息注册到注册中心。
- 发现:查询可用微服务列表及其网络地址。
- 服务检查:定时检测已注册的服务,如发现某实例长时间无法访问,就从注册表中移除。
client功能
- 注册:每个微服务启动时,将自己的网络地址等信息注册到注册中心,注册中心会存储(内存中)这些信息。
- 获取服务注册表:服务消费者从注册中心,查询服务提供者的网络地址,并使用该地址调用服务提供者,为了避免每次都查注册表信息,所以 client 会定时去 serve r拉取注册表信息到缓存到 client 本地。
- 心跳:各个微服务与注册中心通过某种机制(心跳)通信,若注册中心长时间和服务间没有通信,就会注销该实例。
- 调用:实际的服务调用,通过注册表,解析服务名和具体地址的对应关系,找到具体服务的地址,进行实际调用。
Eureka
- Eureka 是 Netflix 开源的一个 Restful 服务,主要用于服务的注册发现。它由两个组件组成:Eureka 服务器 和Eureka 客户端。
- Eureka 服务器用作服务注册服务器。各个节点启动后,会在 Eureka Server 中进行注册,这样 Eureka Server 中的服务注册表中将会存储所有可用服务节点的信息。
- Eureka 客户端是一个 Java 客户端,用来简化与服务器的交互,作为轮询负载均衡器,发现相关的服务,并提供服务的故障切换。
- Eureka 在设计时就优先保证可用性。即在 CAP 理论中,Eureka 满足 AP。Eureka 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。
- Eureka 的几个时间点:
- 在应用启动后,将会向 Eureka Server 发送心跳,默认周期为 30 秒。
- 如果 Eureka Server 在多个心跳周期内没有接收到某个节点的心跳,Eureka Server 将会从服务注册表中把这个服务节点移除(默认90秒)。
- Eureka Client 对已经获取到的注册信息做了 30s 缓存。即服务通过 Eureka 客户端第一次查询到可用服务地址后会将结果缓存,下次再调用时就不会真正向 Eureka 发起 HTTP 请求了。
- 负载均衡组件 Ribbon 也有 30s 缓存。Ribbon 会从上面提到的 Eureka Client 获取服务列表,然后将结果缓存 30s。
- 最大可能出现2分钟的延迟:注册延迟30s + Eureka服务器响应延迟30s + Eureka客户端更新延迟30s + Ribbon服务列表更新延迟30s。
Eureka 启动保护机制:
如果在 15 分钟内超过 85% 的节点都没有正常的心跳,那 么Eureka 就认为客户端与注册中心出现了网络故障。
自我保护机制的触发条件:
条件:当每分钟心跳次数( renewsLastMin ) 小于 numberOfRenewsPerMinThreshold 时,并且开启自动保护模式开关( eureka.server.enable-self-preservation = true ) 时,触发自我保护机制,不再自动过期续约。
其中:
numberOfRenewsPerMinThreshold = expectedNumberOfRenewsPerMin * 续租百分比( eureka.server.renewalPercentThreshold, 默认0.85 )
expectedNumberOfRenewsPerMin = 当前注册的应用实例数 x 2 。为什么乘以 2: 默认情况下,注册的应用实例每半分钟续租一次,那么一分钟心跳两次,因此 x 2 。
解释:服务实例数10个,期望每分钟续约数10 2=20,期望阈值200.85=17,自我保护少于17时 触发。】
Eureka 启动保护机制会出现以下情况:
- Eureka 不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
- Eureka 仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)。
- 当网络稳定时,当前实例新的注册信息会被同步到其它节点中。
架构原理图
Eureka Server
引入依赖 spring-cloud-starter-netflix-eureka-server
, 在启动类 Application
上,添加 @EnableEurekaServer
注解。
如果是单机服务,可以在 application.yml
中使用以下配置:
|
|
启动服务,可在 http://localhost:2000
查看项目页面。
Eureka Client
EurekaClient 可以在客户端获取eureka服务器上的注册者信息。
引入依赖 spring-cloud-starter-netflix-eureka-client
, 在启动类 Application
上,添加 @EnableDiscoveryClient
注解。
在 application.yml
中使用以下配置:
|
|
启动项目即可。可在 http://localhost:2000
中看到注册的状态。
Eureka 高可用
Eureka Server 之间是可以互相注册的。
举个例子,我们有 3 个 Eureka 注册中心,端口分别为 2001 、 2002 和 2003 。那么端口为 2001 的最基本的配置如下:
|
|
端口 2002 和 2003 服务可以根据以上规则配置。
多网卡选择
服务器有多个网卡,eh0,eh1,eh2,只有eh0可以让外部其他服务访问进来,而Eureka client将eh1和eh2注册到Eureka server上,这样其他服务就无法访问该微服务了。有两种方式:
指定IP注册
1234eureka:instance:prefer-ip-address: trueip-address: 实际能访问到的IP使用spring.cloud.inetutils配置网卡选择
Eureka健康检查
由于server和client通过心跳保持 服务状态,而只有状态为UP的服务才能被访问。看eureka界面中的status。
比如心跳一直正常,服务一直UP,但是此服务DB连不上了,无法正常提供服务。
此时,我们需要将 微服务的健康状态也同步到server。只需要启动eureka的健康检查就行。这样微服务就会将自己的健康状态同步到eureka。配置如下即可。
在client端加入Actuator,并配置eureka.client.healthcheck.enabled=true
,将自己真正的健康状态传播到server。
通过代码来改动服务的状态:
|
|
应用场景:比如说短信业务,欠费了等情况,可以暂时下线服务。
Eureka遇到的坑
available-replicas为空的问题
如果使用了eureka.instance.prefer-ip-address: true
,然后eureka.client.service-url.defaultZone
配置的IP与实例IP不一致,会出现available-replicas为空的问题。
解决方法:在eureka.instance.ip-address
中强制设置IP,然后在eureka.client.service-url.defaultZone
配置对应的IP。
Eureka 最佳实践
本人所在的公司由于体量小,生产环境直接使用 Eureka 的默认配置进行高可用性运行,目前也没有出现太大的问题。
以下是一些实践参考文章(注意:文章中的版本号不是最新的,可能在配置上会有调整):
- Spring Cloud中,Eureka常见问题总结
- Eureka Clustering documentation and best practices
- Documentation: changing Eureka renewal frequency WILL break the self-preservation feature of the server