服务发现旨在解决硬编码提供服务地址的问题,想解决这个问题,需要一个强大的服务发现机制,通过这种机制可以获取到服务提供者的网络信息,即使服务提供者的信息发生变化,服务消费者也无需修改配置文件,即通过服务发现组件实现微服务实例的自动化注册与发现。
服务提供者、服务消费者、服务发现组件关系:
综上,服务发现组件具备以下功能:
Spring Cloud提供了多种服务发现组件,如Eureka、Consul和Zookeeper等,下边我们将讲解其中的Eureka组件,其它的组件大家可以从官网文档或网络上的博客文章中学习下。
Spring Cloud Eureka是Spring CLoud Netflix微服务套件中的一部分,是基于Netflix Eureka的二次封装,主要完成微服务建构中的服务治理功能,其本身是一个基于REST的服务。
Spring Cloud Netflix中还包括Ribbon、Hystrix、Fegin和Zuul等,这些组件将在后续的连载文章中介绍
Region和Zone是AWS(Amazon Web Services)的概念,Region标识AWS中的地理位置,每个Regioin包含多个Zone,各个Region之间完全隔离,AWS通过这种方式实现最大的容错和稳定性。
SpringCloud默认使用的Region是us-east-1,在非AWS环境下,可以将Zone理解成机房,将Region理解为跨机房的Eureka集群。
Eureka包含Eureka Server和Eureka Client两部分,作用如下:
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
需要在启动类上添加@EnableEurekaServer注解,声明这是一个Eureka Server
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
spring.application.name=eureka-server
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://local:8761/eureka/
配置文件简单说明:
参数名 | 说明 | 默认值 |
---|---|---|
eureka.client.register-with-eureka | 是否注册到Eureka | true |
eureka.client.fetch-registry | 是否注册到Eureka | true |
eureka.client.serviceUrl.defaultZone | 与Eureka Server交互的地址,多个地址间使用英文逗号分隔 |
由于这里只是一个单机的Eureka Server,因此不存在将自己作为Eureka Client注册到其它Eureka Server,也不存在从Eureka Server获取注册列表,同步其它Eureka Server节点数据的情况,所以这里配置eureka.client.register-with-eureka和eureka.client.fetch-registry都为false,如果是生产环境Eureka Server肯定是高可用的,使用默认配置即可
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
在Spring Cloud Edgware版本之前,需要在启动类上添加@EnableEurekaClient或EnableDiscoveryClient注解,在Edgware之后,只需在pom中添加相关依赖,即可自动注册。
@SpringBootApplication
@EnableEurekaClient
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
spring.application.name=user-service
server.port=8081
eureka.client.serviceUrl.defaultZone=http://local:8761/eureka/
eureka.instance.prefer-ip-address=true
配置说明:
参数名 | 说明 | 默认值 |
---|---|---|
spring.application.name | 注册到Eureka Server上的应用的名称 | |
eureka.instance.prefer-ip-address | 表示将自己的IP注册到Eureka Server而不是hostname | false |
eureka.client.serviceUrl.defaultZone | 与Eureka Server交互的地址,多个地址间使用英文逗号分隔 |
前文的Eureka Server示例,是一个单节点的注册中心,并不适合线上生产环境。因为当Eureka Server宕机时,Eureka Client不能及时更新缓存中的服务列表,当其中的某个微服务实例出现不可用的情况时,就可能影响整个应用系统的稳定性
只需要改造配置文件即可:
spring.application.name=eureka-server
server.port=9120
eureka.instance.lease-renewal-interval-in-seconds=30
eureka.instance.lease-expiration-duration-in-seconds=90
eureka.client.registry-fetch-interval-seconds=30
eureka.client.serviceUrl.defaultZone=http://slave01:9120/eureka/,http://slave02:9120/eureka/
只需要改造配置文件即可:
spring.application.name=user-service
server.port=8081
eureka.instance.lease-renewal-interval-in-seconds=30
eureka.instance.lease-expiration-duration-in-seconds=90
eureka.client.registry-fetch-interval-seconds=30
eureka.client.serviceUrl.defaultZone=http://slave01:9120/eureka/,http://slave02:9120/eureka/
参数名 | 说明 | 默认值 |
---|---|---|
eureka.instance.lease-renewal-interval-in-seconds | Eureka Client发送心跳到Eureka Server的时间间隔 | 30s |
eureka.instance.lease-expiration-duration-in-seconds | Eureka Server接收到最后一个心跳信号后到移除某个实例的等待时间 | 90s |
eureka.client.registry-fetch-interval-seconds | Eureka Client 从Eureka Server上拉取注册信息的时间间隔 | 30s |
之前的示例中,Eureka Server是允许匿名访问的,实际生产项目中,为了提高安全性,我们只允许经过认证校验的实例注册到Eureka
pom文件改造:增加spring-security做安全校验
org.springframework.boot
spring-boot-starter-security
application配置文件新增开启认证、用户名和密码配置,并更改Eureka Server连接路径:
security.basic.enabled=true
security.user.name=admin
security.user.password=admin
eureka.client.serviceUrl.defaultZone=http://admin:admin@slave01:9120/eureka/,http://admin:admin@slave02:9120/eureka/
只需要更改application中的连接方式即可:
eureka.client.serviceUrl.defaultZone=http://admin:admin@slave01:9120/eureka/,http://admin:admin@slave02:9120/eureka/
Eureka包含两种元数据:
常用Rest端点整理:
接口路径 | 功能 | 说明 |
---|---|---|
POST/eureka/apps/appID | 注册服务 | 输入JSON/XML格式的数据;204:成功 |
DELETE/eureka/apps/appID/instanceID | 注销服务 | 200:成功 |
PUT/eureka/apps/appID/instanceID | 发送心跳 | 200:成功、404:instanceID对应的服务不存在 |
GET/eureka/apps | 查询所有实例 | 200:成功;返回JSON/XML |
GET/eureka/apps/appID | 查询appID对应的所有实例 | 200:成功;返回JSON/XML |
GET/eureka/apps/appID/instanceID | 查询appID下instanceID对应的实例 | 200:成功;返回JSON/XML |
GET/eureka/apps/instances/instanceID | 查询指定的实例 | 200:成功;返回JSON/XML |
PUT/eureka/apps/appID/instanceID/metadata?key=value | 更新指定实例的元信息 | 200:成功、500:失败 |
Eureka的通信机制使用HTTP的REST接口实现,由于HTTP的平台无关性,虽然Eureka Server通过Java实现,但是其下的微服务应用不限于使用JAVA来开发。
目前已有很多其它语言平台支持Eureka,如eureka-js-client、python-eureka等,如果使用的语言网络上面没有封装好的客户端,我们还可以自己实现,通过使用过Eureka的REST端点,我们可以实现注册服务、注销服务、心跳、服务发现等
Eureka官网REST API 文档:https://github.com/Netflix/eureka/wiki/Eureka-REST-operations
当我们在本地单机调试时,经常会在Eureka的首页爆出一个警告,这个警告其实就是Eureka的自我保护机制报出来的。
默认情况下,Eureka Server在一定时间内没有接收到某个微服务的心跳,Eureka Server将注销这个实例(默认90s)。但是当网络发生故障时,Eureka Server和微服务之间无法正常通信,上边的注销行为将非常危险,因为微服务本身其实都时健康的,这个时候不应该注销。Eureka通过“自我保护”的模式解决这个问题,当Eureka Server在短时间内丢失过多的客户端时,就会进入“自我保护”的模式,将这些客户端的实例保护起来,在保护期间,如果实例出现问题,那么调用者很可能拿到实际已经不存在的服务实例,从而造成调用失败的情况出现,所以客户端必须要有容错机制,如请求重试、断路器等机制
本地调试时很容易触发注册中心的保护机制,使得注册中心维护的实例不那么准确。所以,本地开发时,可以用eureka.server.enable-self-preservation=false参数关闭保护机制,以确保注册中心将不可用的实例正确剔除。
官方文档:
Eureka Server:https://cloud.spring.io/spring-cloud-static/Edgware.SR5/single/spring-cloud.html#spring-cloud-eureka-server
Eureka Client: https://cloud.spring.io/spring-cloud-static/Edgware.SR5/single/spring-cloud.html#_service_discovery_eureka_clients
参考书籍:
Spring Cloud微服务实战
Spring Cloud与Docker 微服务架构实战