1.服务发现简介
前面文章说了,硬编码提供者地址的方式有不少问题,要想解决这些问题,服务消费者需要一个强大的服务发现机制,服务消费者使用这种机制获取服务提供者的网络信息。不仅如此,即时服务提供者的信息变化,服务消费者也无须修改配置文件.
服务发现组件提供这种能力.在微服务架构中,服务发现组件是一个非常关键的组件,如图
服务提供者丶服务消费者丶服务发现组件这三者之间的关系大致如下:
1.各个微服务在启动时,将自己的网络地址等信息注册到服务发现组件中,服务发现组件会存储这些信息。
2.服务消费者可从服务发现组件查询服务提供者的网络地址,并使用该地址调用服务提供者的接口。
3.各个微服务与服务发现组件使用一定机制(例如心跳)通信。服务发现组件若长时间无法与某微服务实例通信,就会注销该 实例
4.微服务网络地址发生变更(例如实例增减或者IP端口发生变化等)时候,会重新注册到服务发现组件。使用这种方式,服务消 费者就无须人工修改提供者的网络地址了。
综上所述,服务发现组件应具备以下功能:
1.服务注册表: 是服务发现组件的核心,它用来记录各个微服务的信息,例如微服务的名称丶IP丶端口等。服务注册表提供查 询API和管理API,查询API用于查询可用的微服务实例,管理API用于服务的注册和注销。
2.服务注册与服务发现: 服务注册是指微服务在启动时,将自己的信息注册到服务发现组件上的过程。服务发现是指查询可 用微服务列表以及其网络地址的机制。
3.服务检查: 服务发现组件使用一定的机制定时检查已经注册的服务,如发现某个实例长时间无法访问,就会从服务注册表中 移除该实例。
从以上来讲,使用服务发现的好处是显而易见的,Spring Cloud提供了多种服务发现组件的支持,例如Eureka丶Consul和Zookeeper等,
2. Eureka简介
Eureka是Netflix开源的服务发现组件,本身是一个基于REST的服务。它包含Server和Client两部分。Spring Cloud将它集成在子项目Spring Cloud Netflix中,从而实现微服务的注册和发现。
3. Eureka原理
首先理解一下Region和 Availability zone均是AWS的概念,其中Region标识AWS中的地理位置,每个Region都有多个 Availability zone,各个Region之间完全隔离,AWS通过这种方式实现了最大的容错和稳定性。
Spring Cloud默认使用Region是us-east-1,在非AWS环境下,可以将Availability zone理解为机房,将Region理解为跨机房的 Eureka集群
- Application Service 相当于服务提供者
- Application Client 相当于服务消费者
- Make Remote Call 可以理解为调用RESTful Api的行为
- us-east-1c,us-east-1d,us-east-1e等都是zone,它们都属于us-east-1这个region
Eureka包含两个组件: Eureka Server和Eureka Client,他们的作用如下:
- Eureka Server 提供服务发现的能力,各个微服务启动时,会向Eureka Server,注册自己的信息(例如IP丶端口丶微服务名称等),Eureka Server会存储这些信息
- Eureka Client 是一个Java客户端,用于简化与Eureka Server的交互
- 微服务启动后,会周期性(默认30s)地向Eureka Server发送心跳以续约自己的"租期"
- 如果Eureka Server在一定时间内没有接受到某个微服务实例的心跳,Eureka Server将注销该实例(默认90s)
- 默认情况下,Eureka Server 同时也是 Eureka Client ,多个Eureka Server实例相互之间通过复制的方式来实现服务注册表中数据同步。
- Eureka Client 会缓存服务注册表中的信息。这种方式有一定的优势---首先,微服务无需每次请求都查询Eureka Server,从而降低了Eureka Server的压力,其次,即时Eureka Server所有节点都宕掉,服务消费者,依然可以使用缓存中的信息找到服务提供者并完成调用。
综上所述,Eureka通过心跳检测,客户端缓存等机制,提高了系统的灵活性,可伸缩性和可用性。
4.创建Eureka Server
- 创建Maven项目,添加如下依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.spring</groupId> <artifactId>eureka</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Finchley.BUILD-SNAPSHOT</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>
- 编写启动类,在启动类上添加@EnableEurekaServer注解,声明这是一个Eureka Server
@SpringBootApplication @EnableEurekaServer public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } }
- 在配置文件中添加如下内容:
server: port: 8761 servlet: context-path: /eureka eureka: client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://localhost:8761/eureka/
- 测试:
5.Eureka的自我保护模式
默认情况下,如果Eureka Server 在一定的时间内没有接受到某个微服务实例的心跳,Eureka Server将注销该实例(默认90s)。但是当网络分区故障发生时候,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了,------因为微服务本身其实是健康的,此时本不应该注销这个微服务。
Eureka 通过"自我保护模式" 来解决这个问题----当Eurka Server 节点在短时间内丢失过多客户端时,那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server 就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任务微服务)。当网络故障恢复后,该Eureka Server 节点会自动退出自我保护模式。
综上所述,可以让Eureka集群更加的健壮丶稳定。
在Spring Cloud 中,可以使用 eureka.server.enable-self-preservation=false 禁用自我保护模式。
6.Eureka的健康检查
由图可见,在status一栏中有个UP,表示应用程序状态正常,只有标记为"UP"的微服务会被请求,
Eureka Server与Eureka Client 之间通过心跳机制来确定Eureka Client的状态.但是这个机制并不能完全反应应用程序的状态, 比如,微服务与Eureka Server之间的心跳正常,Eureka Server认为该微服务是"UP",然而,该微服务的数据源发生了问题(例如连接不上,网络抖动等),导致无法正常工作,这种情况怎么解决?
可以通过前文说的 Spring Cloud Actuator提供了/health 端点,该端点展示了应用程序的健康信息,怎么样才能将该端点的健康状态传播到Eureka Server呢? 要实现这一点,只需要开启Eureka的健康检查,这样应用程序就会将自己的健康状态传播到Eureka Server,开启很简单只需要在配置文件中添加该配置eureka.client.healthcheck.enabled=true
某些场景下,可能希望更细粒地控制健康检查,此时可实现 com.netflix.appinfo.HealthCheckHandler接口
PS: 如果Eureka Client 连接不上 Eureka Server,显示Cannot execute request on any known server错误,可以看这篇文章,跳转