Eureka 入门教程 - 基础篇

Eureka 介绍

Eureka 是什么

Eureka 是 Netflix 开发的一款基于 HTTP REST 的服务,由 Pivotal 公司将其整合进 Spring Cloud 生态。Netflix 在设计 Eureka 时遵守的是 AP 原则,通常用于服务注册发现、负载均衡和故障转移等,也是 Spring Cloud 中使用的服务注册发现组件。Eureka 采用 C/S 架构,提供了一个基于 Java 的 Client 组件,用来与服务端交互,同时具有一套内置的负载均衡器,可以进行基本的轮询负载均衡。Eureka 从 2012 年 9 月在 Github 上发布 1.1.2 版本以来,至今已经发布了 231 次,最新版本为 2020 年 4 月份发布的 1.9.20 版本。期间有进行 2.x 版本的开发,不过由于各种原因内部已经冻结开发,目前还是以 1.x 版本为主。更多介绍可参考:Eureka 项目Eureka 官方英文文档Spring Cloud Eureka 官方中文文档

Eureka 服务治理体系

eureka-framework-3

Eureka 服务的三个角色

  • 服务注册中心:Eureka 提供的服务端,提供服务注册与发现的功能,一般被称作 Eureka-Server;
  • 服务消费者:消费者应用从服务注册中心获取服务列表,从而使消费者可以知道去何处调用其所需要的服务;
  • 服务提供者:提供服务的应用,可以是 Spring Boot 应用,也可以是其他技术平台且遵循 Eureka 通信机制的应用。它将自己提供的服务注册到 Eureka,以供其他应用发现。

Eureka 高可用性

Eureka 的高可用性

Eureka 的服务注册中心,它和其他服务注册中心一样,支持高可用配置。依托于强一致性提供良好的服务实例可用性,可以应对多种不同的故障场景。Eureka 服务端支持集群模式部署,当集群中有分片发生故障的时候(超过 85% 的服务实例丢失心跳),Eureka 会自动转入自我保护模式。它允许在分片发生故障的时候继续提供服务的发现和注册,当故障恢复时,集群中的其他分片会把各自的状态再次同步回来。集群中的的不同服务注册中心通过异步模式互相复制各自的状态,这也意味着在给定的时间点每个实例关于所有服务的状态可能存在不一致的现象。

Eureka 的自我保护模式

默认情况下,如果 Eureka Server 在一定时间内(默认 90 秒)没有接收到某个微服务实例的心跳,Eureka Server 将会注销该实例。但是当网络分区故障发生时,微服务与 Eureka Server 之间无法正常通信,这就可能变得非常危险了。因为微服务本身是健康的,此时本不应该注销这个微服务。Eureka Server 通过 “自我保护模式” 来解决这个问题,当 Eureka Server 节点在短时间内丢失过多客户端时(超过 85% 的服务实例丢失心跳,可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server 就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该 Eureka Server 节点会自动退出自我保护模式。自我保护模式是一种对网络异常的安全保护措施,使用自我保护模式,可以让 Eureka 集群更加的健壮、稳定。

在自我保护模式中,Eureka Server 会保护注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阀值以上时,该 Eureka Server 节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息(健康或不健康的微服务都会保留),也不盲目注销任何可能健康的服务实例。在 Spring Cloud 中,可以使用 eureka.server.enable-self-preservation: false 来禁用自我保护模式。

Eureka 自我保护模式的效果

  • Eureka 不再从注册列表移除因长时间没收到心跳而应该过期的服务
  • Eureka 仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点(高可用)
  • Eureka 在网络稳定的时候,当前实例新的注册信息会被同步到其他节点中(最终一致性)
  • Eureka 可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像 ZooKeeper 一样使得整个注册中心瘫痪

Eureka 的健康检查

在 Eureka 中,微服务状态可取值为 DOWN、OUT_OF_SERVICE、UNKNOWN 等,只有 UP 的微服务会被请求。由于 Eureka Server 与 Eureka Client 之间使用心跳机制来确定 Eureka Client 的状态,默认情况下服务器端与客户端的心跳保持正常,应用程序就会始终保持 UP 状态,所以微服务的 UP 并不能完全反应应用程序的状态。Spring Boot Actuator 提供了 /health 端点,该端点可展示应用程序的健康信息,只要将该端点中的健康状态传播到 Eureka Server 就可以了,实现这点很简单,只需为微服务配置如下内容即可。如果需要更细粒度健康检查,可实现 com.netflix.appinfo.HealthCheckHandler 接口,EurekaHealthCheckHandler 已实现了该接口。

1
2
# 开启健康检查(需要添加spring-boot-starter-actuator依赖)
eureka.client.healthcheck.enabled = true

Eureka 入门案例

1. 版本说明

以下案例使用了版本较旧的 SpringCloud-Dalston.SR1 + SpringBoot-1.5.9.RELEASE,配置方式跟最新版本的 SpringCloud 有一定的区别

2. 创建 Maven 父级 Pom 工程

在父工程里面配置好工程需要的父级依赖,目的是为了更方便管理与简化配置,具体 Maven 配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<properties>
<springcloud.version>Dalston.SR1</springcloud.version>
<springboot.version>1.5.9.RELEASE</springboot.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${springcloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

3. 创建 Eureka Server 工程

创建 Eureka Server 的 Maven 工程,配置工程里的 pom.xml 文件,只需添加 spring-cloud-starter-eureka-server 即可;若使用最新版本的 SpringCloud,则需要改为添加 spring-cloud-starter-netflix-eureka-server

1
2
3
4
5
6
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>

创建 Eureka Server 的启动主类,这里添加相应注解,作为程序的入口:

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

public static void main(String[] args){
SpringApplication.run(EurekaServerApplication.class, args);
}
}

添加 Eureka Server 需要的 application.yml 配置文件到工程的 src/main/resources 目录下:

1
2
3
4
5
6
7
8
9
10
11
server:
port: 7001

eureka:
instance:
hostname: localhost #Eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己
fetch-registry: false #false表示自己就是注册中心,职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

4. 创建 Eureka Client 工程

创建 Eureka Client 的 Maven 工程(作为服务提供者),配置工程里的 pom.xml 文件,需要添加以下内容。若使用最新版的 SpringCloud,此时只需要引入 spring-cloud-starter-netflix-eureka-client 依赖

1
2
3
4
5
6
7
8
9
10
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>

创建 Eureka Client 的启动主类,添加注解 @EnableEurekaClient

1
2
3
4
5
6
7
8
@EnableEurekaClient
@SpringBootApplication
public class DeptProviderApplication {

public static void main(String[] args){
SpringApplication.run(DeptProviderApplication.class, args);
}
}

添加 Eureka Client 需要的 application.yml 配置文件到工程的 src/main/resources 目录下,特别注意:在生产环境(外网)部署 Eureka Server,一定要配置 eureka:instance:prefer-ip-address: true 参数,否则服务消费者无法通过正确的 IP 调用服务提供者的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 9090

spring:
application:
name: demo-client #需要指定spring.application.name,否则会在 Eureka Server 界面显示为 UNKNOW

eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
instance:
instance-id: demo-client-9090 #自定义服务名称
prefer-ip-address: true #将IP注册到Eureka Server上,若不配置默认使用机器的主机名

5. 完善注册服务的 info 信息

Maven 父级 Pom 工程添加以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<finalName>springcloud-demo</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<delimiters>
<delimit>$</delimit>
</delimiters>
</configuration>
</plugin>
</plugins>
</build>

配置 Eureka Client 工程里的 pom.xml 文件,引入 spring-boot-starter-actuator

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

配置 Eureka Client 工程里的 application.yml 文件,添加以下内容;若 Info 界面不能正确显示 $ 符号替换后的服务信息,可以尝试依次执行 mvn cleanmvn compilemvn install 命令

1
2
3
4
5
info:
app.name: springcloud-demo
company.name: www.example.com
build.version: $project.version$
build.artifactId: $project.artifactId$

6. 测试

分别启动 eureka-server 及 eureka-client 应用,然后通过浏览器访问 http://127.0.0.1:7001,若正常显示 Eureka Server 的管理界面和服务列表(如下图),则说明一切配置成功。

eureka-example-view

Eureka 进阶实战

Eureka 的服务事件监听

Eureka 提供了五种服务监听事件,因为在某些业务场景下,可能需要做一些自定义的扩展;例如某个微服务挂掉了,希望能监听到并给管理员发送邮件通知等。

  • EurekaServerStartedEvent: Eureka 注册中心启动事件
  • EurekaRegistryAvailableEvent: Eureka 注册中心可用事件
  • EurekaInstanceRenewedEvent: 服务实例续约事件
  • EurekaInstanceCanceledEvent: 服务实例下线事件
  • EurekaInstanceRegisteredEvent: 服务实例注册事件

Eureka 的 REST API

Eureka 提供了 REST API,允许非 Java 语言的其他应用服务通过 HTTP REST 的方式接入 Eureka 的服务发现中,API 列表可参考 Eureka 官方文档的介绍或者下图。

eureka-rest-api

Eureka 开启登录认证

配置 Eureka Server 工程里的 pom.xml 文件,引入 spring-cloud-starter-security 依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>

配置 Eureka Server 工程里的 application.yml 文件,增加用户名、密码的配置

1
2
3
4
5
6
security:
basic:
enabled: true
user:
name: admin #Eureka的登录用户名
password: 123456 #Eureka的登录密码

在 Eureka Server 工程里加入 Security 配置类,关闭掉 CSRF,否则 Client 无法连接 Eureka Server 端;若项目中引入了 Actuator,那么还需要放行 Actuator 的请求

1
2
3
4
5
6
7
8
9
10
11
12
13
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/eureka/**").permitAll()
.antMatchers("/actuator/**").permitAll()
.anyRequest().authenticated().and().httpBasic();
}

}

配置 Eureka Client 工程里的 application.yml 文件,更改注册中心的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 9090

spring:
application:
name: demo-client

eureka:
client:
service-url:
defaultZone: http://admin:123456@localhost:7001/eureka
instance:
instance-id: demo-client-9090
prefer-ip-address: true

分别启动 eureka-server 及 eureka-client 应用,然后通过浏览器访问 http://127.0.0.1:7001,此时会先弹出登录框,输入正确的用户名和密码后才能看到管理页面

Eureka 集群配置

假设现有三台 Eureka Server 主机,每台主机的 IP 与端口分别是: 192.168.1.105:8005192.168.1.106:8006192.168.1.107:8007

Eureka Server1 配置

1
2
3
4
5
6
7
8
9
10
11
server:
port: 8005

eureka:
instance:
hostname: 192.168.1.105
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://192.168.1.106:8006/eureka/,http://192.168.1.107:8007/eureka/

Eureka Server2 配置

1
2
3
4
5
6
7
8
9
10
11
server:
port: 8006

eureka:
instance:
hostname: 192.168.1.106
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://192.168.1.105:8005/eureka/,http://192.168.1.107:8007/eureka/

Eureka Server3 配置

1
2
3
4
5
6
7
8
9
10
11
server:
port: 8007

eureka:
instance:
hostname: 192.168.1.107
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://192.168.1.105:8005/eureka/,http://192.168.1.106:8006/eureka/

Eureka Client 集群配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 9090

spring:
application:
name: demo-client

eureka:
client:
service-url:
defaultZone: http://192.168.1.105:8005/eureka/,http://192.168.1.106:8006/eureka/,http://192.168.1.107:8007/eureka/
instance:
instance-id: demo-client-9090
prefer-ip-address: true

启动三台 Eureka Server,分别访问三台 Eureka Server 的管理界面,若界面上的 DS Replicas 项可以正常显示其他 Eureka Server 节点(如下图),则说明 Eureka 服务器集群配置成功

eureka-server-cluster

本地集群搭建的细节说明

若三台 Eureka Server 的 IP 都是 127.0.0.1 或者 localhost,彼此只是服务端口不一样;此时建议修改系统的 hosts 文件作相应的域名映射,方便日后访问 Eureka 的服务。当 hosts 文件加入下述配置之后,则可以通过不同的域名访问对应的 Eureka 服务了,如:http://eureka8005.com:8005/eureka/

1
2
3
127.0.0.1 eureka8005.com
127.0.0.1 eureka8006.com
127.0.0.1 eureka8007.com

特别注意:新版的 Eureka 搭建集群时,eureka.client.serviceUrl.defaultZone 配置项的地址,不能使用 localhost 或者内网/外网 IP,必须使用域名,DNS 解析需自行配置,也可以在本机的 /etc/hosts 里映射域名,否则各节点均出现在 unavailable-replicas

补充内容

CAP 理论

CAP 理论的核心是:一个分布式系统不可能同时很好地满足一致性、可用性和分区容错性这三个需求。因此,根据 CAP 理论可以将 NoSQL 数据库分成满足 CA 原则、满足 CP 原则和满足 AP 原则三大类:

  • CA - 单点集群,满足一致性、可用性的系统,通常在可扩展上不太强大
  • CP - 满足一致性,分区容错性的系统,通常性能不是特别高
  • AP - 满足可用性、分区容忍性的系统,通常可能对一致性要求低一点

eureka-cap

Eureka 对比 ZooKeeper

  • ZooKeeper 保证的是 CP,Eureka 保证的是 AP
  • Eureka 本质上是一个工程,而 ZooKeeper 只是一个进程
  • ZooKeeper 有 Leader 和 Follower 角色,Eureka 各个节点是平等关系
  • ZooKeeper 在选举期间注册服务瘫痪,虽然服务最终会恢复,但是选举期间不可用;Eureka 只要有一实例就可以保证服务可用,但查询到的数据可能并不是最新的
  • ZooKeeper 采用过半数存活原则,Eureka 采用自我保护机制解决分区问题

Netflix 在 AWS 中的 Eureka 部署架构

eureka-framework

上图(左边)描述的是 Netflix 在 AWS 中的 Eureka 部署架构,图中的 us-east-1x 指的是不同的 zone。AWS 将服务划分成不同地区(region),每个 region 中又有若干个机房(zone),结构图大致上图(右边)所示,每个 zone 都是一个 Eureka 集群,其中至少有一台 Eureka Server,用来处理 zone failure。在 Eureka 中注册的服务每隔 30s 会向服务端发送一次心跳,用来告知服务端自己是否” 存活”,这个过程就是图中的 renew;如果 renew 操作在重试几次后都没有成功,那这个服务在 90s 之内就会被踢除。需要注意的是,renew 信息和服务注册信息会在多个 zone 间同步,任何一个 zone 中的客户端都可以寻找到任意一个 zone 中注册的服务信息。

两个 @EnableXXXClient 注解的区别

  • Spring Cloud 提供了 @EnableDiscoveryClient@EnableEurekaClient 注解,其中 @EnableDiscoveryClient 基于 spring-cloud-commons,而 @EnableEurekaClient 基于 spring-cloud-netflix
  • Spring Cloud 的服务注册发现有多种实现(Eureka、Consul、Zookeeper 等),如果选用的注册中心是 Eureka,那么就推荐使用 @EnableEurekaClient,如果是其他的注册中心,那么推荐使用 @EnableDiscoveryClient
  • 特别注意:@EnableEurekaClient 上包含了 @EnableDiscoveryClient,可以说 @EnableEurekaClient 拥有 @EnableDiscoveryClient 的功能,其实 @EnableEurekaClient 就是一种方便使用 Eureka 的注解而已