Eureka

Eureka简介

是Netflix开源的服务发现组件,本事是基于REST的服务. 包括Server和Client两部分.在Spring Cloud子项目Spring Cloud Netflix中.

Eureka能做什么?

微服务的注册与发现

Eureka什么时候使用?有哪些应用场景?在Spring Cloud中是一个什么位置的存在?

Eureka Server 和 Eureka Client

  • Eureka Server : 提供服务发现的能力,各服务启动时,会向Eureka Server注册自己的信息(IP,端口,服务信息等),Eureka Server会存储这些信息.
  • Eureka Client : 服务提供者,简化与Eureka Server的交互.
  • 微服务启动后,会周期性(默认30秒)的向Eureka Server发送心跳以续约自己的”租期”.
  • 如果Eureka Server在一定时间内没有收到某个微服务实例(Eureka Client)的心跳,Eureka Server将会注销该实例(默认90秒).
    注 :
  • 默认请求下,Eureka Server同时也是Eureka Client.多个Eureka Server实例,互相之间通过复制的方式,来实现服务注册表中的数据.(实现集群,高可用)
  • Eureka Client会缓存注册表中的信息.这种方式有一定的优势
    首先,微服务无需每次请求都查询Eureka Server,从而降低了Eureka Server的压力;
    其次,即使Eureka Server所有节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者并完成调用.
    这样,Eureka通过心跳检测,客户端缓存等机制,提高了系统的灵活性,可伸缩性和可用性.

Eureka Server示例

创建Maven项目,pom.xml 配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.cloud</groupId>
<artifactId>spring-cloud-eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-cloud-eureka-server</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.RELEASE</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>
</project>

如果使用IntelliJ IDEA




启动类中加入@EnableEurekaServer

@EnableEurekaServer : 表示这是一个Eureka Server

application.yml

注 : 默认生成的项目配置文件是 application.properties,这里我是使用application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
# 服务注册中心 (单节点)
server:
port: 1111

eureka:
instance:
hostname: localhost
client:
fetch-registry: false # 表示是否从Eureka Server获取注册信息,默认为true.因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,这里设置为false
register-with-eureka: false # 表示是否将自己注册到Eureka Server,默认为true.由于当前应用就是Eureka Server,故而设置为false.
service-url:
# 设置与Eureka Server的地址,查询服务和注册服务都需要依赖这个地址.默认是http://localhost:8761/eureka/;多个地址可使用','风格.
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

eureka的配置信息就是这个org.springframework.cloud.netflix.eureka.EurekaClientConfigBean类,具体可以看源码

启动Eureka Server工程,打开浏览器输入地址即可看到eureka的信息

Eureka Client示例

重复之前的步骤

1
2
3
<artifactId>spring-cloud-eureka-server</artifactId>
改成
<artifactId>spring-cloud-eureka-client</artifactId>

启动类中加入@EnableDiscoveryClient

之前启动类的@EnableEurekaServer换成@EnableDiscoveryClient
@EnableDiscoveryClient : 声明这是一个Eureka Client

application.yml

Eureka Client的application.yml如下

1
2
3
4
5
6
7
8
9
10
11
12

server:
port: 2222

spring:
application:
name: service

eureka:
client:
service-url:
defaultZone: http://localhost:1111/eureka # 指定服务注册中心

启动Eureka Client工程

再访问Eureka Server http://localhost:1111 就可以看到,注册的服务了.

这样一个简单的微服务就被注册到Eureka Server.

Eureka Server的高可用(集群)

以上的Eureka Server只是简单Demo,真正生产环境是需要高可用的,通常会部署一个高可用的Eureka Server集群.
Eureka Server可以通过运行多个实例并相互注册的方式实现高可用部署,Eureka Server实例会彼此增量地同步信息,从而保证所有节点数据一致.
事实上,节点之间互相注册是Eureka Server默认行为.
之前的Eureka Server配置如下,

1
2
3
4
5
eureka:
client:
# 下面两个配置默认值是true,也就是,节点之间互相注册是Eureka Server默认行为.所以集群的时候,不设置即可
# fetch-registry: false # 表示是否从Eureka Server获取注册信息,默认为true.因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,这里设置为false
# register-with-eureka: false # 表示是否将自己注册到Eureka Server,默认为true.由于当前应用就是Eureka Server,故而设置为false.

Eureka Server集群示例

  1. 复制之前Eureka Server项目,修改pom.xml中artifactId,这个你自己填写
  2. 修改hosts,加入 127.0.0.1 server-1 server-2
  3. 增加 application-server-1.yml application-server-2.yml

application-server-1.yml配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
# 服务注册中心 (多节点,集群)
server:
port: 1111

eureka:
instance:
hostname: server-1
client:
service-url:
defaultZone: http://server-2:1112/eureka/

# 需要修改host 127.0.0.1 server-1
# java -jar --spring.profiles.active=server-1

application-server-2.yml配置如下

1
2
3
4
5
6
7
8
9
10
11
12
# 服务注册中心 (多节点,集群)
server:
port: 1112

eureka:
instance:
hostname: server-2
client:
service-url:
defaultZone: http://server-1:1111/eureka/
# 需要修改host 127.0.0.1 server-2
# java -jar --spring.profiles.active=server-2

  1. 然后打包项目,使用java命令启动
    java -jar aidijing-eureka-server-0.0.1-SNAPSHOT.jar –spring.profiles.active=server-1
    java -jar aidijing-eureka-server-0.0.1-SNAPSHOT.jar –spring.profiles.active=server-2

然后再访问,即可看到如下图

Eureka Client注册到Eureka Server集群上

在之前Eureka Client的配置文件中修改application.yml配置即可

1
2
3
4
eureka:
client:
service-url:
defaultZone: http://localhost:1111/eureka/,http://localhost:1112/eureka/ # 指定多个服务注册中心

启动的时候,端口号自行指定两个不同的就行了

Eureka Server安全

为Eureka Server加入用户认证,默认Eureka Server是匿名的.

spring-boot-starter-security依赖

在之前的Eureka Server项目中加入spring-boot-starter-security,当然也可以复制一个新的项目进行添加.

1
2
3
4
5
<!-- 为Eureka Server添加用户认证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

application.yml 中添加spring相关配置

1
2
3
4
5
6
security:
basic:
enabled: true # 开启基于Http basic认证
user:
name: root
password: root

这样再启动Eureka Server,访问时则需要进行认证才可以进入

Eureka Client注册到需要认证的Eureka Server

很简单只需要修改之前的配置,对defaultZone进行修改
格式是 : http://user:password@EurekaHost:EurekaPort/eureka/

1
2
3
4
5
6
eureka:
client:
service-url:
# defaultZone: http://localhost:1111/eureka/ # 指定服务注册中心
# 格式是 : http://user:password@EurekaHost:EurekaPort/eureka/
defaultZone: http://root:root@localhost:1111/eureka/ # 指定服务注册中心

Eureka元数据

Eureka的元数据有两种

  1. 标准元数据
    指的是主机名,IP地址,端口号,状态页和健康检查等信息,这些信息都会被发布在注册表中,用于服务之间的调用
  2. 自定义元数据
    可以使用eureka.instance.metadata-map进行配置,这些元数据可以在远程客户端中访问,但不会改变客户端行为.
    自定义元数据,就是可以在标准元数据上添加一些其他信息

    自定义元数据示例

    在原来Eureka Client上进行测试
    application.yml新增如下配置
    1
    2
    3
    4
    5
    6
    eureka:
    # ... ...
    instance:
    metadata-map:
    # 自定义元数据,key/value自行定义
    order-id: 自定义元数据

打印元数据

在Eureka Client项目控制器中新增一个方法,打印元数据信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private Registration registration;

// LogUtils是我自己封装的日志类,其实就是LogManager.getLogger(),另外我用的是log4j2
@GetMapping( "metadata" )
public void testMetadata () {
final List< ServiceInstance > instances = discoveryClient.getInstances( registration.getServiceId() );
instances.forEach( service -> LogUtils.getLogger().info(
"host:{}, service_id:{},metadata:{}",
service.getHost(),
service.getServiceId(),
service.getMetadata()
) );
return;
}

然后访问这个控制器,得到如下信息.

Eureka 的自我保护模式

进入自我保护模式最直观的体现是,Eureka Server首页输出的警告,如下图

默认情况下,如果Eureka Server在一定时间内没有收到某个微服务实例的心跳,Eureka Server就会注销这个实例(默认是90秒).
但是当网路分区发生故障时,微服务与Eureka Server之间无法正常通信,以上行为就可能非常危险,因为微服务本身是健康的,此时本不应该注销这个服务的.

Eureka 通过自我保护模式来解决这个问题,当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式.
一旦进入这个模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不注销任何微服务).当网络故障恢复后,这个Eureka Server
节点会自动退出自我保护模式.

当然也可以关闭自我模式

1
2
3
eureka:
server:
enable-self-preservation: false # 禁用自我保护模式

多网卡下的IP选择

对于多网卡的服务器,各个微服务注册到Eureka Server上的IP要如何指定呢?

指定IP在某些场景下很有用.例如某台服务器有eth0,eth1,eth2三张网卡,但是只有eth1可以被其他的服务器访问;
如果Eureka Client将eth0或者eth2注册到Eureka Server上,其他微服务就无法通过这个IP调用该微服务的接口.
Spring Cloud提供了按需选择IP的能力,从而避免以上的问题.

忽然指定名称的网卡

1
2
3
4
5
6
7
8
spring:
cloud:
inetutils:
ignored-interfaces:
- docker0 # 忽略docker0网卡
- veth.* # 忽略所有以veth开头的网卡.
instance:
prefer-ip-address: true

使用正则表达式忽略

1
2
3
4
5
6
7
8
spring:
cloud:
inetutils:
ignored-interfaces:
- 192.168
- 10.0
instance:
prefer-ip-address: true

只使用站点本地地址

1
2
3
4
5
6
spring:
cloud:
inetutils:
use-only-site-local-interfaces: true # 强制使用站点本地地址
instance:
prefer-ip-address: true

手动指定IP地址

1
2
3
4
spring:
instance:
prefer-ip-address: true
ip-address: 127.0.0.1

Eureka 的健康检查

Eureka Server与Eureka Client之间使用心跳机制来确定Eureka Client的状态,默认情况下,服务端与客户端
保持正常,应用程序就会始终保持’UP’状态.以上机制并不能完全反应应用程序的状态.
如果,微服务与Eureka Server之间的心跳正常,Eureka Server认为该服务’UP’;然而,该微服务的数据源发生了
问题(例如因为网络抖动,连不上数据源),那么其实这个微服务是无法正常工作的.

Spring Boot Actuator提供了/heath端点,改端点可展示应用程序的健康信息.
那么如何才能将该端点中的健康状态传播到Eureka Server呢?

要实现这一点,只需启动Eureka的健康检查.这样应用程序就会将自己的健康状态传播到Eureka Server.开启的方法非常简单,只需
为微服务配置一下内容,就可以开启健康检查.

1
2
3
4
# actuator 权限    
management:
security:
enabled: false

未完成

服务注册发现实现了,但是这样依然有问题,比如负载均衡.各位服务之间会部署多个实例,那么服务消费者要如何将请求分摊
到多个服务器实例上呢?

.