SpringCloud Eureka是什么?
Eureka
英 [juˈri:kə]
字面意思是发现、找到的意思。
Eureka
是Netflix
开发的服务发现框架,本身是一个基于REST
的服务,主要用于定位运行在AWS
域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。Spring Cloud
将它集成在其子项目spring-cloud-netflix
中,以实现Spring Cloud的
服务发现功能。
Eureka
采用了 C-S
的设计架构。Eureka Server
作为服务注册功能的服务器,它是服务注册中心。
Eureka
和Dubbo
的架构对比图:
- Eureka
- dubbo
Eureka
包含两个组件:Eureka Server
和Eureka Client
Eureka Server
提供服务注册服务- 各个节点启动后,会在
Eureka Server
中进行注册,这样Eureka Server
中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到 Eureka Client
是一个Java
客户端,用于简化Eureka Server
的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server
发送心跳(默认周期为30秒)。如果Eureka Server
在多个心跳周期内没有接收到某个节点的心跳,Eureka Server
将会从服务注册表中把这个服务节点移除(默认90秒)。
Eureka的三大角色
Eureka Server
提供服务注册和发现。Service Provider
服务提供方将自身服务注册到Eureka
,从而使服务消费方能够找到。Service Consume
r服务消费方从Eureka
获取注册服务列表,从而能够消费服务。
Eureka的构建和使用
创建Eureka注册中心模块
POM
文件如下
<?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">
<parent>
<artifactId>user-management</artifactId>
<groupId>com.zealzhangz</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>microservice-cloud-eureka-7001</artifactId>
<dependencies>
<!--eureka-server服务端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<!-- 修改后立即生效,热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</project>
- 入口代码
/**
* @version Version: 1.0
* @date DateTime: 2018/08/18 23:42:00<br/>
*/
@SpringBootApplication
@EnableEurekaServer // EurekaServer服务器端启动类,接受其它微服务注册进来
public class EurekaServer7001App {
public static void main(String[] args) {
SpringApplication.run(EurekaServer7001App.class,args);
}
}
- 启动并验证启动成功
# 直接浏览器访问端口
http://127.0.0.1:7001
将已有的服务注册到注册中心
将microservice-cloud-provider-user-8001
服务注册到注册中心
- 修改
POM
文件添加依赖
<!-- 将微服务provider侧注册进eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
- 修改配置文件
eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://localhost:7001/eureka
- 添加服务发现注解
@EnableEurekaClient
package com.zealzhangz;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @version Version: 1.0
* @date DateTime: 2018/08/15 00:44:00<br/>
*/
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
@SpringBootApplication
public class UserProvider8001App {
public static void main(String[] args) {
SpringApplication.run(UserProvider8001App.class,args);
}
}
- 测试服务注册结果
直接在浏览器访问下面
url
,确认服务正确注册
http://localhost:7001
完善我们的微服务
目前我们已经完成了注册功能,但是此时我们刷新一下http://localhost:7001/ 这个页面就会看到一段红色的字
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
- 这个是
Eureka
的自我保护的一个机制。 那么为什么Eureka
会有这种自我保护的机制呢,原因有以下几点?- 因为长时间没有另外的一些服务访问,也就是说没有心跳。
- 服务名没有变更,已经有的服务现在没了。
某时刻某一个微服务不可用了,
eureka
不会立刻清理,依旧会对该微服务的信息进行保存。那么我们怎么解决呢?
- 采用
actuator
与注册微服务信息进行完善actuator
是什么? actuator
是SpringBoot
里面主要用来主管和监控和配置
我们先来完善我们的微服务再来解决这种Eureka自我保护的机制
- 修改主机名
在
microservice-cloud-provider-user-8001
的application.yml
文件里面最后一行添加如下配置:
eureka:
instance:
instance-id: microservicecloud-user-8001
prefer-ip-address: true #访问路径可以显示IP地址
微服务info内容详细信息
- 修改
microservice-cloud-provider-user-8001 POM
文件
<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 修改总工程pom文件添加构建
build
信息 添加内容:
<build>
<finalName>micro-service-cloud</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><!--注意不要使用$,原因是${}会被maven处理-->
</delimiters>
</configuration>
</plugin>
</plugins>
</build>
- 再修改
microservice-cloud-provider-user-8001
工程下的application.yml
文件,来添加我们这个微服务的一些描述
info:
app.name: zhanga-micro-service-cloud
company.name: www.zhangaoo.com #我的博客地址
build.artifactId: @project.artifactId@ #注意不要使用$,原因是${}会被maven处理
build.version: @project.version@
访问对应的接口http://192.168.50.95:8001/info
,就会返回以下JSON
结果
{
"app": {
"name": "zhanga-micro-service-cloud"
},
"company": {
"name": "www.zhangaoo.com"
},
"build": {
"artifactId": "microservicecloud-provider-user-8001",
"version": "1.0-SNAPSHOT"
}
}
Eureka的自我保护机制介绍
默认情况下,如果Eureka Server
在一定时间内没有接收到某个微服务实例的心跳,Eureka Server
将会注销该实例(默认90
秒)。但是当网络分区故障发生时,微服务与Eureka Server
之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka
通过“自我保护模式”来解决这个问题——当Eureka Server
节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server
就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server
节点会自动退出自我保护模式。
到的心跳数重新恢复到阈值以上时,该Eureka Server
节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着。
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka
集群更加的健壮、稳定。
在Spring Cloud
中,可以使用eureka.server.enable-self-preservation = false
禁用自我保护模式。
server:
enable-self-preservation: false
但是eureka
自我保护模式希望大家在没有其他特殊业务需求的话就不要去禁用它的自我保护模式
Eure的服务发现
对于注册进eureka
里面的微服务,可以通过服务发现来获得该服务的信息
- 在UserController中加入下面代码
@Autowired
private DiscoveryClient discoveryClient;
@RequestMapping(value = "/discovery",method = RequestMethod.GET)
public Object discover(){
List<String> list = discoveryClient.getServices();
log.info("list:" + JSONObject.toJSONString(list));
List<ServiceInstance> srvList = discoveryClient.getInstances("MICROSERVICECLOUD-USER");
for (ServiceInstance element : srvList) {
//然后打印你指定要找的微服务的ID和主机和端口以及访问地址等信息
log.info(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t" + element.getUri());
}
return this.discoveryClient;
}
- 测试接口结果
访问接口
http://127.0.0.1:8001/user/discovery
结果如下
{
"localServiceInstance": {
"host": "192.168.50.95",
"port": 8001,
"serviceId": "microservicecloud-user",
"metadata": {},
"uri": "http://192.168.50.95:8001",
"secure": false
},
"services": [
"microservicecloud-user"
]
}
- 给服务定义静态
metadata
在上面的JSON返回结果中metadata
为空,我们可以通过配置文件定义静态metadata
eureka:
instance:
metadata-map:
fixed-s1: "value_1"
再次请求返回结果如下:
{
"services": [
"microservicecloud-eureka"
],
"localServiceInstance": {
"host": "127.0.0.1",
"port": 8001,
"uri": "http://127.0.0.1:8001",
"serviceId": "microservicecloud-user",
"metadata": {
"fixed-s1": "value_1"
},
"secure": false
}
}
- Dynamic Service Metadata
可在代码中动态定义metadata
@Component
public class DynamicMetadataReporter {
@Autowired
private ApplicationInfoManager applicationInfoManager;
@PostConstruct
public void init() {
Map<String, String> map = applicationInfoManager.getInfo().getMetadata();
map.put("dynamic-s1", "value_2");
}
}
结果如下:
[
{
"host": "127.0.0.1",
"port": 8001,
"uri": "http://127.0.0.1:8001",
"serviceId": "MICROSERVICECLOUD-USER",
"metadata": {
"dynamic-s1": "value_2",
"fixed-s1": "value_1"
},
....
}
]
Eureka集群配置
-
按照
microservice-cloud-eureka-7001
配置分别在创建microservice-cloud-eureka-7002
和microservice-cloud-eureka-7003
两个模块 -
修改3台
eureka
服务器的application.yml
配置
- 7001的配置
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/
defaultZone: http://localhost:7002/eureka/,http://localhost:7003/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
- 7002的配置
server:
port: 7002
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7003/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
- 7003的配置
server:
port: 7003
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
- 修改
microservice-cloud-provider-user-8001
配置文件
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
instance:
instance-id: microservicecloud-user-8001
prefer-ip-address: true #访问路径可以显示IP地址
ip-address: 127.0.0.1
按上述配置配置以后发现会有下面问题
- Eureka Server间无法同步数据,具体表现是每个Eureka Server上的续约数都不一样,同时在General Info标签下别的Eureka Server显示为”unavailable-replicas”。
- DS Replicas标签下面也不显示任何东西
针对上面的两个问题,需要修改两点
Eureka
集群默认使用hostname,因此需要在hosts文件映射一下Eureka
集群各个Eureka Server
需要互相发现,因此需要设置register-with-eureka: true
和fetch-registry: true
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
register-with-eureka: true
fetch-registry: true
- 重新配置如下
- hosts配置配置
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
- 7001的配置
spring:
application:
name: microservicecloud-eureka
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
# prefer-ip-address: true
# ip-address: 127.0.0.1
client:
register-with-eureka: true #false表示不向注册中心注册自己。
fetch-registry: true #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
# 单机环境设置为 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
- 7002的配置
spring:
application:
name: microservicecloud-eureka
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
# prefer-ip-address: true
# ip-address: 127.0.0.1
client:
register-with-eureka: true #false表示不向注册中心注册自己。
fetch-registry: true #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
- 7003的配置
spring:
application:
name: microservicecloud-eureka
server:
port: 7003
eureka:
instance:
hostname: eureka7003.com #eureka服务端的实例名称
# prefer-ip-address: true
# ip-address: 127.0.0.1
client:
register-with-eureka: true #false表示不向注册中心注册自己。
fetch-registry: true #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
microservice-cloud-provider-user-8001
的配置
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: microservicecloud-user-8001
prefer-ip-address: true #访问路径可以显示IP地址
ip-address: 127.0.0.1
-
注意:因为开启了
register-with-eureka: true
和fetch-registry: true
,如果其中一个eureka server
连接不上的话就会异常,默认每10秒钟会重试连接一次。因此这个异常在eureka
集群中有节点不可用的话是正常的。 -
一个完整的截图如下,集群中的每个
eureka
都会同步集群中的信息
Eureka相比Zookeeper有哪些优势呢?
著名的
CAP
理论指出,一个分布式系统不可能同时满足C
(一致性)、A
(可用性)和P
(分区容错性)。由于分区容错性P
在是分布式系统中必须要保证的,因此我们只能在A
和C
之间进行权衡。
- Zookeeper保证的是CP。
- Eureka则是AP。
Zookeeper
保证CP
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down
掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zk
会出现这样一种情况,当master
节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader
选举。问题在于,选举leader
的时间太长,30 ~ 120s, 且选举期间整个zk
集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk
集群失去master
节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
Eureka
保证AP
Eureka
看明白了这一点,因此在设计时就优先保证可用性。Eureka
各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka
的客户端在向某个Eureka
注册或时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,Eureka
还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka
就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
Eureka
不再从注册列表中移除因为长时间没收到心跳而应该过期的服务Eureka
仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)- 当网络稳定时,当前实例新的注册信息会被同步到其它节点中
因此, Eureka
可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper
那样使整个注册服务瘫痪
本文由 zealzhangz 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为:
2018/08/30 11:13