SpringCloud系列之一---搭建高可用的Eureka注册中心
前言
本篇文章主要介绍的是SpringCloud相关知识、微服务架构以及搭建服务注册与发现的服务模块(Eureka)以及Eureka集群。
GitHub源码链接位于文章底部。
什么是SpringCloud
Spring Cloud 是一系列框架的有序集合。 它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发, 如服务发现注册、配置中心、消息总线、负载均衡、熔断器、数据监控等,都可以用 Spring Boot 的开发风格做到一键启动和部署。Spring 并没有复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过 SpringBoot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
SpringCloud 与 SpringBoot 的关系
Spring Boot是Spring的一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的云应用开发工具;Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;Spring Boot 使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现,可以不基于Spring Boot吗?不可以。
Spring Boot可以离开Spring Cloud独立使用开发项目, 但是Spring Cloud离不开Spring Boot,属于依赖的关系。
SpringCloud 主要组件
| 用途 | 组件|
| ------------ |
| 服务发现|Netflix Eureka |
| 服务调用|Netflix Feign |
| 熔断器|Netflix Hystrix |
| 服务网关|Netflix Zuul |
| 分布式配置|Spring Cloud Config |
|消息总线| Spring Cloud Bus|
SpringCloud 与 Dubbo 对比
dubbo由于是二进制的传输,占用带宽会更少。
springCloud是http协议传输,带宽会比较多,同时使用http协议一般会使用JSON报文,消耗会更大。
dubbo的开发难度较大,原因是dubbo的jar包依赖问题很多大型工程无法解决。
Dubbo只是实现了服务治理,而Spring Cloud下面有很多个子项目分别覆盖了微服务架构下的方方面面,服务治理只是其中的一个方面,一定程度来说,Dubbo 只是 Spring CloudNetflix 中的一个子集。
Dubbo | SpringCloud | |
---|---|---|
服务注册中心 | Zookeeper | Spring Cloud Netflix Eureka |
服务调用方式 | RPC | REST API |
服务网关 | 无 | Spring Cloud Netflix Zuul /Spring Cloud GateWay |
熔断器 | 不完善 | Spring Cloud Netflix Hystrix |
分布式配置 | 无 | Spring Cloud Config |
服务跟踪 | 无 | Spring Cloud Sleuth |
消息总线 | 无 | Spring Cloud Bus |
数据流 | 无 | Spring Cloud Stream |
批量任务 | 无 | Spring Cloud Task |
...... | ...... | ...... |
SpringCloud 的版本
SpringCloud 由于是一系列框架组合,为了避免与包含的自框架版本产生混淆,采用伦敦地铁站的名称作为版本名,形式为版本名+里程碑号。M9为第 9 个里程碑版本。以下是SpringBoot与Spring Cloud版本的对照表。
Spring Boot | Spring Cloud |
---|---|
1.2.x | Angel 版本 |
1.3.x | Brixton 版本 |
1.4.x | Camden 版本 |
1.5.x | Dalston 版本、 Edgware 版本 |
2.0.x | Finchley 版本 |
服务发现组件 Eureka
Eureka是Netflix 开发的服务发现框架,SpringCloud将它集成在自己的子项目spring-cloud-netflix中,实现SpringCloud的服务发现功能。Eureka包含两个组件:Eureka Server和Eureka Client。
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
自我保护机制
Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就别一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。但是在短时间内丢失大量的实例心跳,这时候EurekaServer会开启自我保护机制,Eureka不会踢出该服务,这就是Eureka的自我保护机制。
产生原因:在开发测试时,需要频繁地重启微服务实例,但是我们很少会把eureka server一起重启(因为在开发过程中不会修改eureka注册中心),当一分钟内收到的心跳数大量减少时,会触发该保护机制。可以在eureka管理界面看到Renews threshold和Renews(last min),当后者(最后一分钟收到的心跳数)小于前者(心跳阈值)的时候,触发保护机制,会出现红色的警告:
EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT.RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEGING EXPIRED JUST TO BE SAFE.
从警告中可以看到,eureka认为虽然收不到实例的心跳,但它认为实例还是健康的,eureka会保护这些实例,不会把它们从注册表中删掉。
该保护机制的目的是避免网络连接故障,在发生网络故障时,微服务和注册中心之间无法正常通信,但服务本身是健康的,不应该注销该服务,如果eureka因网络故障而把微服务误删了,那即使网络恢复了,该微服务也不会重新注册到eureka server了,因为只有在微服务启动的时候才会发起注册请求,后面只会发送心跳和服务列表请求,这样的话,该实例虽然是运行着,但永远不会被其它服务所感知。所以,eureka server在短时间内丢失过多的客户端心跳时,会进入自我保护模式,该模式下,eureka会保护注册表中的信息,不在注销任何微服务,当网络故障恢复后,eureka会自动退出保护模式。自我保护模式可以让集群更加健壮。
但是我们在开发测试阶段,需要频繁地重启发布,如果触发了保护机制,则旧的服务实例没有被删除,这时请求有可能跑到旧的实例中,而该实例已经关闭了,这就导致请求错误,影响开发测试。所以,在开发测试阶段,我们可以把自我保护模式关闭,只需在eureka server配置文件中加上如下配置即可:
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客户端与 Eureka 服务端进行交互的地址
defaultZone: http://127.0.0.1:${server.port}/eureka
#是否将自己注册到Eureka服务中,本身就是注册中心所以无需注册
register-with-eureka: false
#是否从Eureka中检索注册信息,本身就是注册中心所以无需检索
fetch-registry: false
server:
# 测试时关闭自我保护机制,保证不可用服务及时踢出
enable-self-preservation: false
##剔除失效服务间隔
eviction-interval-timer-in-ms: 2000
在eureka client配置文件中加上:
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客户端与 Eureka 服务端进行交互的地址
defaultZone: http://127.0.0.1:8100/eureka/
# 心跳检测检测与续约时间
# 测试时将值设置设置小些,保证服务关闭后注册中心能及时踢出服务
instance:
# Eureka客户端向服务端发送心跳的时间间隔,单位为秒(客户端告诉服务端自己会按照该规则)
lease-renewal-interval-in-seconds: 1
# Eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,超过则剔除(客户端告诉服务端按照此规则等待自己)
lease-expiration-duration-in-seconds: 2
但在生产环境,不会频繁重启,所以,一定要把自我保护机制打开,否则网络一旦中断,就无法恢复。
当然关于自我保护还有很多个性化配置,这里不详细说明。
注意考虑网络不可达情况下:调用接口幂等、重试、补偿等。
创建工程
1. 目前工程结构
2. 首先创建父工程springcloud,以后这个工程下存放子工程eureka,feign,zuul等组件
将该工程的src文件夹删除,在pom文件中添加依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath ></relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!--引用仓库-->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<!--管理依赖,子项目中的依赖不用列出版本号-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.M9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
创建eureka父工程
新建springcloud-eureka项目,在pom文件中添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
创建eureka服务端
以springcloud-eureka为父工程,新建springboot-eureka-server项目,这里不需要添加eureka-server依赖,因为父工程中有了。
1. 添加配置
在resources目录中添加application.yml文件,在文件中添加配置
#服务端口号
server:
port: 8100
spring:
application:
name: eureka-server
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客户端与 Eureka 服务端进行交互的地址
defaultZone: http://127.0.0.1:${server.port}/eureka
#是否将自己注册到Eureka服务中,本身就是注册中心所以无需注册
register-with-eureka: false
#是否从Eureka中检索注册信息,本身就是注册中心所以无需检索
fetch-registry: false
server:
# 测试时关闭自我保护机制,保证不可用服务及时踢出
enable-self-preservation: false
##剔除失效服务间隔
eviction-interval-timer-in-ms: 2000
2. 启动类
java目录下创建com.lxg二级目录,然后创建EurekaServerApp启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer {
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class, args);
}
}
在类上加EnableEurekaServer注解,启动EurekaServer。
创建eureka客户端
以springcloud-eureka为父工程,新建springboot-eureka-client项目。在pom文件中添加依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
1. 添加配置
在resources目录中添加application.yml文件,在文件中添加配置
#端口号
server:
port: 9100
spring:
application:
name: eureka-client
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客户端与 Eureka 服务端进行交互的地址
defaultZone: http://127.0.0.1:8100/eureka/
# 心跳检测检测与续约时间
# 测试时将值设置设置小些,保证服务关闭后注册中心能及时踢出服务
instance:
# Eureka客户端向服务端发送心跳的时间间隔,单位为秒(客户端告诉服务端自己会按照该规则)
lease-renewal-interval-in-seconds: 1
# Eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,超过则剔除(客户端告诉服务端按照此规则等待自己)
lease-expiration-duration-in-seconds: 2
2. 启动类
java目录下创建com.lxg二级目录,然后创建EurekaClientApp启动类,这里启动类名称不能为EurekaClient,否则会起冲突导致启动失败。类上使用EnableEurekaClient注解。
@SpringBootApplication
@EnableEurekaClient
public class EurekaClientApp {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApp.class, args);
}
}
测试
先启动服务端,再启动客户端,访问127.0.0.1:8100 ,访问监控页面。
图中的红色字体是在提示已经关闭了eureka的自我保护机制,此时如果关闭客户端,页面中的服务就会被踢出,如果没有关闭的话,即使客户端关闭了,服务依然会存在。
高可用注册中心(Eureka集群)
在微服务中,注册中心非常核心,可以实现服务治理,如果一旦注册出现故障的时候,可能会导致整个微服务无法访问,在这时候就需要对注册中心实现高可用集群模式。
Eureka高可用原理
默认情况下Eureka是让服务注册中心,不注册自己,但是在集群中,需要设置能注册自己,因为这两个属性默认为true,只需要不写就行了。
###使该注册中心注册自己
register-with-eureka: true
###需要去注册中心上检索服务
fetch-registry: true
Eureka高可用实际上将自己作为服务向其他服务注册中心注册自己,这样就可以形成一组相互注册的服务注册中心,从而实现服务清单的互相同步,达到高可用效果。
Eureka集群环境搭建
新增server1,server2,server3三个节点。
server1配置:
#服务端口号
server:
port: 8100
spring:
application:
name: eureka-server
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客户端与 Eureka 服务端进行交互的地址
defaultZone: http://127.0.0.1:8200/eureka,http://127.0.0.1:8300/eureka
server:
# 测试时关闭自我保护机制,保证不可用服务及时踢出
enable-self-preservation: false
##剔除失效服务间隔
eviction-interval-timer-in-ms: 2000
server2配置:
#服务端口号
server:
port: 8200
spring:
application:
name: eureka-server
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客户端与 Eureka 服务端进行交互的地址
defaultZone: http://127.0.0.1:8100/eureka,http://127.0.0.1:8300/eureka
server:
# 测试时关闭自我保护机制,保证不可用服务及时踢出
enable-self-preservation: false
##剔除失效服务间隔
eviction-interval-timer-in-ms: 2000
server3配置:
#服务端口号
server:
port: 8300
spring:
application:
name: eureka-server
#eureka基本配置信息
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8100/eureka,http://127.0.0.1:8200/eureka
server:
# 测试时关闭自我保护机制,保证不可用服务及时踢出
enable-self-preservation: false
##剔除失效服务间隔
eviction-interval-timer-in-ms: 2000
然后修改客户端的配置,因为以前是单个eureka注册中心,只需要注册进一个地址就行了,现在要注册进所有的注册中心。
#端口号
server:
port: 9100
spring:
application:
name: eureka-client
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客户端与 Eureka 服务端进行交互的地址
#单机
#defaultZone: http://127.0.0.1:8100/eureka/
#集群
defaultZone: http://127.0.0.1:8100/eureka/,http://127.0.0.1:8200/eureka/,http://127.0.0.1:8300/eureka/
# 心跳检测检测与续约时间
# 测试时将值设置设置小些,保证服务关闭后注册中心能及时踢出服务
instance:
# Eureka客户端向服务端发送心跳的时间间隔,单位为秒(客户端告诉服务端自己会按照该规则)
lease-renewal-interval-in-seconds: 1
# Eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,超过则剔除(客户端告诉服务端按照此规则等待自己)
lease-expiration-duration-in-seconds: 2
先启动所有的服务端节点,再启动客户端。访问localhost:8100 ,localhost:8200 ,localhost:8300 都能进入eureka的界面,同时看到客户端服务和其他的注册中心。这个时候即使有某一个节点挂了,服务依然是可用的,而且性能肯定比单机版要好。
搭建eureka集群有几点需要注意的:
1.与先前独立运行注册不同,注意defaultZone属性,它的值为除了自己以外的所有eureka节点的地址,以英文逗号分割。
2.去掉fetch-registry 与 register-with-eureka配置(其实这样做就会取对应的默认值,两个值均为true),需要让自己能被注册和检索。
3.启动第一个注册中心时会报Cannot execute request on any known server的错误,暂时不管它,实际上eureka注册中心的ui界面是能打开的,当所有的节点启动完毕,就能找到服务,此错误就会消失。
4.所有注册中心的节点的spring.application.name必须保持一致。
5.当客户端需要往注册中心集群注册服务时defaultZone属性需要把所有节点地址都加上,如果像单节点一样的话,你连接的那个节点挂了,集群中其他节点就无法获取到该服务,也就不能达到高可用。
本文GitHub源码:https://github.com/lixianguo5097/springcloud/tree/master/springcloud-eureka
CSDN:https://blog.csdn.net/qq_27682773
简书:https://www.jianshu.com/u/e99381e6886e
博客园:https://www.cnblogs.com/lixianguo