编辑推荐: |
本文来自于个人微博,本文主要介绍了
一套基于 Spring Boot 实现的微服务开发工具 Spring Cloud-Eureka
服务器端如何配置部署开发,希望对您的学习有所帮助。 |
|
Spring Cloud 是一套基于 Spring Boot 实现的微服务开发工具
。 微服务(也称微服务架构) , 简单的说 , 就是将一个系统按照一定的规则有效的拆分成多个不同的服务
, 每个服务都能够独立的进行开发 、 部署 、 扩展和维护 。 服务与服务之间可以通过 RESTful
API 等方式进行相互调用 。
Spring Cloud 没有重复制造轮子 , 它只是将业界内多个开源的微服务框架集成起来 , 再通过
Spring Boot 进行包装屏蔽掉了复杂的配置和实现原理 , 目的是给开发者予一套简单易懂 、
易部署和易维护的分布式系统开发工具包 。 它提供了微服务开发所需的配置管理 、 服务发现 、 断路器
、 智能路由 、 微代理 、 控制总线等组件 。
Eureka
Eureka 是一种基于 REST 的服务 , 主要用于定位服务 , 以实现中间层服务器的负载均衡和故障转移
。 它是由 Spring Cloud Netflix(Spring Cloud 的子项目) 项目提供的
。
Spring Cloud Netflix
它主要是对 Netflix 开源的一系列产品进行包装 , 为 Spring Boot 应用程序提供自动配置的
Netflix OSS 集成 。 通过一些简单的注解 , 就能快速启用并构建大型的分布式系统 。
它提供的模块有:服务发现(Eureka)
断路器(Hystrix)
智能路由(Zuul)
客户端负载均衡(Ribbon)
项目结构
项目地址: https:// github .com / fanlychie
/ spring - cloud - netflix - eureka - sample
开发环境:Java-8 、 Maven-3 、 IntelliJ IDEA-2017
、 Spring Cloud-Dalston . SR1
服务注册中心
本示例对应样例项目中的 eureka-server 项目 , 在
pom.xml 中声明使用 spring - cloud -starter-eureka-server
启动器:
org.springframework.cloud
spring-cloud-starter-eureka-server |
使用 @EnableEurekaServer 注解将应用声明为 Eureka 服务器端(Eureka
Server) , 从而启动 Eureka 服务注册中心的组件 , 对外提供服务注册和发现的功能 。
它默认监听 8761 端口来接收服务注册 , 除此之外它还提供一个可视化的直观页面 , 可以方便的查看注册的服务
。
@SpringBootApplication
@EnableEurekaServer
public class Application{
public static void main(String[] args){
SpringApplication.run(Application.class);
}
} |
在默认的模式中 , Eureka 服务器端也充当 Eureka 客户端 , 并向给定的 serviceUrl
服务器端注册自己 。
在单点模式中 , 需要禁 Eureka 服务器端的客户端行为 。 具体配置如下:
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
|
Eureka 服务器端在启动时需要为其指定 Zone , 如果没有指定 , 默认使用 defaultZone
, 即 eureka.client.service-url.defaultZone 配置的值 。
Region 与 Zone
在 Eureka 中有分 Region(区域)和 Zone(Availability Zone ,
可用区) 。
这是由于 Netflix 开源的 Eureka 旨在 AWS(Amazon Web Services
1.现在通常称为云计算)中运行
2.因此使用了一些 AWS 特有的概念术语
3.Amazon EC2(Amazon Elastic Compute
Cloud
4.亚马逊弹性计算云)托管在全球的多个地方
5.这些地方由 Region 和 Zone 来组成
6.每个 Region 都是完全独立的(你可以简单的理解成是 Eureka
集群)
7.每个 Region 里面有多个隔离的区域称为 Zone(你可以简单的理解成是机房)
8.Region 中的每个 Zone 都是隔离的
但是它们可以通过低延迟的链接来进行联系
运行 Application , 访问: http://localhost:8761/
从上图可以看到 , 此时还没有任何服务注册到 Eureka 服务器 。
客户端
本示例对应样例项目中的 order-service 项目 , 在 pom.xml 中声明使用 spring-cloud-starter-eureka-server
启动器:
org.springframework.cloud
spring-cloud-starter-eureka-server |
使用 @EnableEurekaClient (或者 @EnableDiscoveryClient
) 注解将应用声明为 Eureka 客户端(Eureka Client) 。 Eureka 客户端应用程序细分为两种
, 服务提供者(Applicaton Service)和服务消费者(Application Client)
。 这两个概念下文有涉及 。
@SpringBootApplication
@EnableEurekaClient
public class Application{
public static void main(String[] args){
SpringApplication.run(Application.class);
}
} |
除此之外 , 还需要配置才能找到 Eureka 服务器:
server:
port: 8881
spring:
application:
name: order-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/ |
spring.application.name 是 Eureka 客户端向服务器注册的服务ID和虚拟主机的名称
。 如果没有配置则为 UNKNOWN 。
在 Eureka 服务器中 , 服务ID相同的实例将集群在一起 。
Eureka 客户端在启动时也需要为其指定 Zone , 它会优先请求自己所在的
Zone 的 Eureka 服务器 获取服务的注册列表 。 如果没有指定 , 默认使用 defaultZone
, 即 eureka .client .service-url .defaultZone 配置的值
。
运行 Application , 再次访问: http://localhost:8761/
从上图可以看到 , 客户端应用程序已经成功被注册了 。
高可用
以上示例的 Eureka Server(即上述的服务注册中心)是单点运行的 。 Eureka Server
支持同时运行多个实例 , 每个 Eureka Server 都可以作为客户端向其他的 Eureka
Server 注册自己 , 即它们之间可以进行两两互相注册 。
上图是 Eureka 官方给出的部署架构图 。 下面对这个架构图来作一些解读 , 或许可以帮助你理解
。
Region 与 Zones
这是一个 Eureka 集群的架构图 , 这里面有 1 个 Region(us-east) , 3
个 Zone(us-east-1c 、 us-east-1d 、 us-east-1e) 。 每个
Zone 都是隔离的 。
Eureka Server
每个 Zone 至少有一个 Eureka Server , 对外提供服务发现和处理区域故障 。
在 Eureka Server 集群中(
eureka.client.register-with-eureka 需设置为 true)
1.没有 Master/Slave 的区分
2.每个 Eureka Server 都是对等(Peer)的
3.它们除了可以作为服务注册中心外还可以充当客户端向其他 Eureka
Server 注册自己
4.并且会从它的对等的节点(由
eureka.client.service-url.defaultZone 配置指定)中 Replicate(复制)所有的服务注册表信息以达到同步的目的
5.如果因为某种原因导致同步失败
6.默认等待 5 分钟(可以通过
eureka.server.wait-time-in-ms-when-sync-empt 配置)
7.在这期间
8.它不向客户端提供服务注册信息
9.并且默认失败重试 5 次(可以通过
eureka.server.number-of-replication-retries配置)
Eureka Client
Eureka 客户端应用程序分为两种 , Applicaton Service(服务提供者)和 Application
Client(服务消费者) 。
Applicaton Service(服务提供者)通常需要向给定的 serviceUrl 对应的
Eureka Server 来 Register(注册)自己 , 以供外部应用可以发现自己 。 其注册信息包含主机名和端口信息等元数据
。 然后默认以每隔 30 秒的频率向注册的 Eureka Server 发送一次心跳(可以通过 eureka.instance.lease-renewal-interval-in-seconds
配置)来 Renew(续约)服务 。
Eureka Server 默认为 90 秒内如果没有收到客户端的心跳 , 则它会将该客户端实例从它的注册表中剔除
, 以禁止该实例的流量(可以通过 eureka.instance.lease-expiration-duration-in-seconds
配置 。 注意 , 如果该值设置的太大 , 即使实例已经不存在了 , 流量也可以路由到该实例;如果设置的太小
, 很可能因为网络问题导致实例被服务器剔除;该值至少应该比发送心跳频率的间隔值要大) 。
Eureka 客户端默认会从注册的 Eureka Server 中获取所有的服务注册表信息(可以通过
eureka.client.fetch-registry 配置) , 默认是以每隔 30 秒的频率去
Get Registry(获取注册表) 一次(可以通过 eureka .client .registry
- fetch - interval - seconds 配置) 。
Application Client(服务消费者)可以不向任何 Eureka
Server 注册自己, 它可以只从 Eureka Server 获取注册过的服务列表,通过 RESTful
API 的方式远程调用 Applicaton Service(服务提供者)
Eureka Server 高可用样例
本示例是在同一主机运行多个 Eureka Server 实例 , 由于 Eureka 会过滤同一主机的相同主机名(详见
com.netflix.eureka.cluster.PeerEurekaNodes#isThisMyUrl
) , 但是它不检查端口 , 因此需要先行定义至少两个不同的主机名 , 并使它们映射到 127.0.0.1
。 这里采用修改 hosts 文件的方式 。 找到并打开系统的 hosts 文件(windows 系统的路径为
C:WindowsSystem32driversetchosts ) , 在最后添加如下行:
127.0.0.1 peer1
peer2 peer3 |
修改 eureka-server 项目的配置文件 src / main
/ resources / application.yml :
spring:
application:
name: eureka-server
profiles:
active: peer1
---
spring:
profiles: peer1
server:
port: 8761
eureka:
instance:
hostname: peer1
client:
service-url:
defaultZone: http://peer2:8762/eureka/,http://peer3:8763/eureka/
---
spring:
profiles: peer2
server:
port: 8762
eureka:
instance:
hostname: peer2
client:
service-url:
defaultZone: http://peer1:8761/eureka/,http://peer3:8763/eureka/
---
spring:
profiles: peer3
server:
port: 8763
eureka:
instance:
hostname: peer3
client:
service-url:
defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/
|
这里配置了 3 个 Eureka Server 实例 , 每个实例与其他两个实例分别进行两两的相互注册
, 关系如图示:
需要注意的是 , Eureka Server 的服务注册信息不能进行二次传播 。 如下图的实例关系配置是不可取的:
此图的每个 Eureka Server 实例是单向的向另外一个实例注册
, 假如现有一个新的客户端实例 C 向 1 注册 , 那么 , 1 和 2 中都会有 C 的注册信息
, 但是 3 中是没有 C 的注册信息的 。
运行 3 个 Eureka Server 实例:
# 运行 peer1 实例
java -jar eureka-server-0.0.1-SNAPSHOT.jar
# 运行 peer2 实例
java -jar -Dspring.profiles.active=peer2 eureka-server-0.0.1-SNAPSHOT.jar
# 运行 peer3 实例
java -jar -Dspring.profiles.active=peer3 eureka-server-0.0.1-SNAPSHOT.jar
|
Eureka Client 高可用样例
修改 order-service 项目的配置文件 src/main/resources/application.yml
:
spring:
application:
name: order-service
profiles:
active: client1
eureka:
client:
service-url:
defaultZone: http://peer1:8761/eureka/
---
spring:
profiles: client1
server:
port: 8881
---
spring:
profiles: client2
server:
port: 8882 |
客户端的 eureka.client.service-url.defaultZone 指定为当前 Zone
中任意一台服务注册中心的地址就可以 , 因为上例中配置的每台服务注册中心的服务注册表是相互进行复制的
。
运行 2 个 Eureka Client 实例:
# 运行 client1
实例
java -jar order-service-0.0.1-SNAPSHOT.jar
# 运行 client2 实例
java -jar -Dspring.profiles.active=client2 order-service-0.0.1-SNAPSHOT.jar
|
访问 http://localhost:8761/ (或 http://localhost:8762/
、 http :// localhost : 8763 / ):
自我保护模式
Eureka 默认开启了自我保护模式(可以通过 eureka .server
.enable-self-preservation 配置) 。 该模式被激活的条件是:在 1 分钟后
, Renews (last min) < Renews threshold 。 你可以在 Eureka
Server 首页的右上侧可以看到:
Renews threshold 与 Renews (last min)
服务器端的续约阀值(Renews threshold)
this.expectedNumberOfRenewsPerMin
= count * 2;
this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin
* serverConfig.getRenewalPercentThreshold());
|
其中 , count 为 服务器的数量 。 2 为每 30 秒 1
个心跳 , 每分钟 2 个心跳的固定频率因。
归纳出的公式为: 2M * renewalPercentThreshold
, M 为服务器的个数 , renewalPercentThreshold 默认为 0.85(可以通过
eureka .server . renewal-percent-threshold 配置) , 计算结果只保留整数位
。
其实这就是个固定值 , 对于每个 Eureka Server 来说 , M 只能取 1 。 代码达到的效果是:
1.expectedNumberOfRenewsPerMin 重置为固定值 2;
2.numberOfRenewsPerMinThreshold 的值被设置为 1;
客户端的续约阀值(Renews threshold)
if (this.expectedNumberOfRenewsPerMin
> 0) {
this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin
+ 2;
this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin
* serverConfig.getRenewalPercentThreshold());
} |
注:上面贴出的 PeerAwareInstanceRegistryImpl 继承自 AbstractInstanceRegistry
。
它们共享 expectedNumberOfRenewsPerMin
和 numberOfRenewsPerMinThreshold 属性,
具体可自行翻阅源码。
设有 N 个客户端:服务器端先启动 , expectedNumberOfRenewsPerMin
被重置为固定值 2 。 接着客户端依次启动:
N = 1 –> (2 + 2) * renewalPercentThreshold
N = 2 –> (2 + 2 + 2) * renewalPercentThreshold
N = 3 –> (2 + 2 + 2 + 2) * renewalPercentThreshold
归纳出的公式为: 2(N + 1) * renewalPercentThreshold , 计算结果只保留整数位
。
即 , 如果只有 1 个 Eureka Server 或者有多个 Eureka Server 但它们之间没有相互注册:
当 N = 0 时 , 只计算服务器端 。 Renews threshold = 1 。 由于没有客户端向服务器发送心跳
, Renews (last min) < Renews threshold , Eureka
自我保护模式被激活;
当 N ≠ 0 时 , 服务器端的计算结果被客户端覆盖 , 即只计算客户端;
当 N = 2 时 , Renews threshold = 2(N + 1) * renewalPercentThreshold
= 2 * 3 * 0.85 = 5 。 2 个客户端以每 30 秒发送 1 个心跳 , 1 分钟后总共向服务器发送
4 个心跳 , Renews (last min) < Renews threshold ,
Eureka 自我保护模式被激活;
所以如果 N < 3 , 在 1 分钟后 , 服务器端收到的客户端实例续约的总数总是小于期望的阀值
, 因此 Eureka 的自我保护模式自动被激活 。 首页会出现警告信息:
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 Server 没有对等的节点 , 同步不到服务注册信息 , 默认需等待
5 分钟(可以通过 eureka.server.wait-time-in-ms-when-sync-empty
配置) 。 即 5 分钟之后你应该看到此警告信息 。
为避免这种情况发生 , 你可以:
关闭自我保护模式( eureka.server.enable-self-preservation
设为 false)
降低 renewalPercentThreshold 的比例( eureka.server.renewal-percent-threshold
设置为 0.5 以下 , 比如 0.49)
部署多个 Eureka Server 并开启其客户端行为( eureka.client.register-with-eureka
不要设为 false , 默认为 true)
如果是采取部署多个 Eureka Server 并开启其客户端行为使其相互注册 。 假设有 M 个
Eureka Server , 那么 , 每个 Eureka Server 每分钟可以额外收到 2
* (M – 1) 个心跳 。 例如:
当 M = 1 , N = 2 时 , Renews threshold = 2(N + 1) *
renewalPercentThreshold = 2 * 3 * 0.85 = 5 , 2 个客户端以每
30 秒发送 1 个心跳 , 1 分钟后总共向服务器发送 4 个心跳 , Renews (last
min) < Renews threshold ;
当 M = 2 , N = 2 时 , Renews threshold = 2(N + 1) *
renewalPercentThreshold = 2 * 3 * 0.85 = 5 , 2 个客户端以每
30 秒发送 1 个心跳 , 1 分钟后总共向服务器发送 4 个心跳 , 另外还有 1 个 M 发来的
2 个心跳 , 总共是 6 个心跳 , Renews (last min) > Renews
threshold ;
Eureka 的自我保护模式是有意义的 , 该模式被激活后 , 它不会从注册列表中剔除因长时间没收到心跳导致租期过期的服务
, 而是等待修复 , 直到心跳恢复正常之后 , 它自动退出自我保护模式 。 这种模式旨在避免因网络分区故障导致服务不可用的问题
。 例如 , 两个客户端实例 C1 和 C2 的连通性是良好的 , 但是由于网络故障 , C2 未能及时向
Eureka 发送心跳续约 , 这时候 Eureka 不能简单的将 C2 从注册表中剔除 。 因为如果剔除了
, C1 就无法从 Eureka 服务器中获取 C2 注册的服务 , 但是这时候 C2 服务是可用的
。 所以 , Eureka 的自我保护模式最好还是开启它 。
|