一、概念
1.1、什么是服务治理
Spring Cloud 封装了 Netflix
公司开发的 Eureka 模块来实现服务治理
服务治理就是提供了微服务架构中各微服务实例的快速上线或下线且保持各服务能正常通信的能力的方案总称。
服务治理的优点:
- 更高的可用性:服务治理可以支持动态的服务实例集群环境,任何服务实例可以随时上线或下线。并且
当一个服务实例不可用时,治理服务器可以将请求转给其他服务提供者
,当一个新的服务实例上线时,也能够快速地分担服务调用请求。 - 负载均衡:服务治理可以提供动态的负载均衡功能,可以
将所有请求动态地分布到其所管理的所有服务实例中进行处理
。 - 提升应用的弹性:服务治理的客户端会
定时从服务治理服务器中复制一份服务实例信息缓存到本地中
,这样即使当服务治理服务器不可用时,服务消费者也可以使用本地的缓存去访问相应的服务
,而不至于中断服务。通过这种机制,极大地提高了应用的弹性。 - 高可用性集群:可以构建服务治理集群,
通过互相注册机制
,将每个治理服务器所管辖的服务信息列表进行交换
,使服务治理服务拥有更高的可用性。
1.2、 什么是Eureka
Eureak
是Netflix
开源微服务框架中一系列项目中的一个。Spring Cloud
对其进行了二次封装,形成了Spring Cloud Netflix
子项目,但未对Netflix
微服务实现原理进行更改,只是进行了Spring Boot
化,使开发者更容易使用和整合。
在Eureka
中,对于服务治理有如下3个概念:
- 服务治理服务器(Eureka服务器) :Eureka采用了CS的设计架构,Eureka Server 作为服务注册功能的服务器,也就是服务注册中心(这里的Eureka Server指的是我们自己专门写一个Java应用来引用Eureka Server的依赖,将这个应用作为注册中心)。而系统中的其他微服务,使用 Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。
- 服务注册代理(服务提供者):如果一个微服务是一个服务提供者,那么可以通过服务注册代理将服务配置信息注册到治理服务器上。服务注册代理可以理解为一个Eureka客户端,负责将微服务所提供的服务向Eureka服务器执行注册、续约和注销等操作,以使服务消费者可以发现并进行消费。在服务注册时需要向服务治理服务器提供服务名称、宿主服务器IP地址、服务端口号、域名等主要数据。
- 服务发现客户端(服务消费者):也是一个Eureka客户端。它在启动时会默认从所服务治理服务器中获取所有的服务注册表信息,通过所获取到的服务注册列表信息来消费相应的服务。
1.3、 Eureka包含两个组件
Eureka Server提供注册服务功能
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
EurekaClient通过注册中心进行访问
EurekaClient可以分为提供者和消费者,他们都是一个Java客户端,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
1.4、 什么场景使用Eureka
准确来说是什么场景需要使用注册中心:
大并发量的应用情况下就需要搭建集群(这里的集群指的不是Eureka注册中心集群,而是指的微服务各个模块的集群),搭建集群我们就需要通过注册中心来实时掌握每个应用的情况,如果根本没有大并发场景,项目虽然也拆分了服务,但是用不到集群,那我们大可不必使用注册中心,使用注册中心从某种意义上来讲,增加了程序的复杂度,而且还得时刻维护注册中心,有点大材小用了。
1.5、 Eureka停更
关于停更问题我在这篇博客已经讲的很清楚了:https://blog.csdn.net/weixin_43888891/article/details/125193088?spm=1001.2014.3001.5501
1.6、代码要实现的内容
二、单机Eureka构建步骤
使用Eureka那就意味着一定是微服务架构,这就涉及到了聚合工程,本篇博客的代码完全是基于本人上一篇博客 搭建的聚合工程 来进行开发的。
idea聚合工程搭建详解:https://blog.csdn.net/weixin_43888891/article/details
2.1、搭建Eureka Server
1、建Module:cloud-eureka-server7001
2、改pom.xml
<?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>mscloud</artifactId><groupId>com.gzl.cn</groupId><version>1.0-SNAPSHOT</version></parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-eureka-server7001</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.gzl.cn</groupId>
<artifactId>cloud-api-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--boot web actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般通用配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>
3、写YML
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
#false表示不向注册中心注册自己。
register-with-eureka: false
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
4、主启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaSever7001Application {
public static void main(String[] args) {
SpringApplication.run(EurekaSever7001Application.class, args);
}
}
5、启动测试
//localhost:7001/
2.2、搭建Eureka Client端的提供者
这里我们直接接着上一篇文章中的cloud-provider-payment8001服务进行调整
1、在原有cloud-provider-payment8001服务的pom当中添加以下依赖
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、添加yml
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
3、启动类添加注解
@EnableEurekaClient
4、测试
先要启动EurekaServer确保 //localhost:7001/ 可以访问 然后再启动cloud-provider-payment8001
微服务注册名配置说明:
5、自我保护机制
下面一排红字不用管,他是Eureka的自我保护机制,在最文章后面我会具体讲他的作用。
2.3、搭建Eureka Client端的消费者
这里我们直接接着上一篇文章中的cloud-provider-payment8001服务进行调整
1、在原有cloud-consumer-order80服务的pom当中添加以下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2、添加yml
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
defaultZone: http://localhost:7001/eureka
3、启动类添加注解
@EnableEurekaClient
4、测试
先要启动EurekaServer, 7001服务 再启动服务提供者provider, 8001服务 最后启动服务消费者consumer,80服务 访问://localhost/consumer/payment/get/1
三、Eureka集群
通过上面的练习,我们会发现根本没体会到Eureka作用是什么,没有Eureka服务照样可以运行使用,这也就是上面提到的,假如微服务不使用集群的话,完全没必要使用注册中心。下面我们进行分别搭建Eureka集群,和微服务集群。
3.1、Eureka集群原理说明
微服务RPC远程服务调用最核心的是什么
高可用,试想你的注册中心只有一个only one, 它出故障了那就呵呵( ̄▽ ̄)"了,会导致整个为服务环境不可用,所以需要搭建Eureka集群
3.2、EurekaServer集群环境构建步骤
1、参考cloud-eureka-server7001 新建 cloud-eureka-server7002(直接复制就行)
2、改pom
3、父工程的pom当中添加model
正常通过ide创建,他是会自动在父工程添加model的,而我们是复制的所以需要手动添加
4、修改启动名字:EurekaSever7002Application
5、修改映射配置
添加:
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
6、修改yml(7001和7002都需要修改),由于7002是直接复制的,所以7002的yml端口号需要改成7002
7002主要是修改端口和hostname,,7001的yml当中的hostname修改为eureka7001.com
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
7、将 支付服务8001微服务 和 订单服务80微服务 发布到上面2台Eureka集群配置中(两个服务都需要修改yml)
http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
8、测试
先要启动EurekaServer, 7001/7002服务 //eureka7001.com:7001/ 再要启动服务提供者provider, 8001 再要启动消费者,80 访问://localhost/consumer/payment/get/1
3.3、支付服务提供者8001集群环境构建
1、参考cloud-provider-payment8001 新建 cloud-provider-payment8002(直接复制就行) 2、改pom
3、父工程的pom当中添加model
4、修改启动名字:PaymentMain8002 5、修改yml(主要就是修改端口号为8002) 6、修改8001/8002的Controller
主要是方便区分我们调用的是哪个服务
3.4、调整80消费服务
1、订单服务访问地址不能写死
// 通过在eureka上注册过的微服务名称调用
public static final String PaymentSrv_URL = "http://CLOUD-PAYMENT-SERVICE";
2、使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
spring-cloud-starter-netflix-eureka-client
依赖当中就依赖了ribbon
依赖,所以我们不用添加ribbon
依赖就能使用他的注解。
一旦地址改为以服务名称访问的形式"//CLOUD-PAYMENT-SERVICE
",就必须添加这个负载均衡的注解,不然访问80服务的接口就会报错。
3、测试
先要启动EurekaServer, 7001/7002服务再要启动服务提供者provider, 8001/8002服务然后最后启动80服务访问://localhost/consumer/payment/get/31 多次访问这个接口,返回的日志当中的端口号会交替出现,这就是负载均衡。当服务提供者8001和8002任意一个挂掉之后,并不会影响服务调用。Ribbon和Eureka整合后Consumer可以直接调用服务而不用再关心地址和端口号,且该服务还有负载功能了。
3.5、Eureka集群严重问题
在测试过程发现的问题,所以记录一下!
访问://eureka7001.com:7001/
访问://eureka7002.com:7002/
这时候会发现所有服务全注册到了7001,而7002实际上并没有服务,当我们把7001服务关闭后,会发现过一段时间服务自动就注册到了7002,可能需要个几秒,这几秒的过程当中如果调用服务是会报错的!
为什么会都优先注册到7001而不是7002呢?跟配置文件写的顺序有关!
假设7001注册中心挂掉的同时8001服务也挂掉了,然后先恢复7001服务,然后再恢复8001服务。这时候会出现以下情况。8001注册到了7001注册中心,而其他没有挂掉的服务,由于7001挂掉了之后注册在7001的服务全部转移到了7002注册中心。
这个时候会引发一个非常严重的问题,就是服务提供者的集群失效!
多次访问80服务的接口,会发现一直是调用的8002服务,他不会去调用8001,因为80服务注册在了7002注册中心,而8001注册在了7001注册中心,服务都不在一个注册中心,所以他根本调用不了。
解决办法: 假如注册中心和一个服务挂掉之后,一定要先想办法先恢复服务,而不是先恢复挂掉的注册中心!先恢复8001服务的话,他会直接注册到7002注册中心,就完全避免了这样的问题。当然这里启动8001服务的时候会报错但是不影响的,因为他启动的时候会先去注册到7001,然后发现7001挂掉了这时候会报错,然后自动注册到7002。
从这个问题也间接的会发现,微服务框架当中,有时候程序启动的先后顺序至关重要!
3.6、主机名称修改 和 ip提示
1、主机名称修改
这里一个是应用名称,一个是主机名称
2、ip提示
当我们服务多了,有时候以应用名称根本无法快速锁定他在哪台服务器部署,所以可以添加以下配置。
3.7、服务发现Discovery
对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息
在8001的PaymentController添加一个接口:
DiscoveryClient 导包的时候导下面的包
import org.springframework.cloud.client.discovery.DiscoveryClient;
@Resource
private DiscoveryClient discoveryClient;
@GetMapping(value = "/payment/discovery")
public Object discovery() {
// 获取注册的服务
List<String> services = discoveryClient.getServices();
for (String element : services) {
System.out.println(element);
}
// 根据服务名称获取服务详细信息
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance element : instances) {
System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
+ element.getUri());
}
return this.discoveryClient;
}
测试://localhost:8001/payment/discovery 访问后看控制台输出内容!
四、Eureka心跳机制
4.1、什么是心跳机制
在了解保护机制之前,我们有必要先了解一下Eureka的心跳机制。
使用注册中心,那我们的服务信息就需要注册到注册中心,注册中心起到的作用是服务治理,那他就需要时刻掌握服务是否宕机,心跳机制就是Eureka client一旦注册到注册中心后,就需要每隔一段时间告诉Eureka server一下,我还是活的。
4.2、Eureka Client心跳频率配置
在每一个Eureka Client启动的时候,都会有一个HeartbeatThread的心跳线程,这个线程的作用就是保证默认30秒
的时候向Eureka Server发送一个信息的请求,告诉Eureka Server当前的Eureka Client还存活着。
下面这个参数可以来配置心跳间隔时间(这个是在Eureka的client端配置的):
eureka:
instance:
lease-renewal-interval-in-seconds: 30
4.3、Eureka Server端接收到心跳做了什么操作
Eureka Server在接收到请求之后,肯定是先去自己的注册表中去,找到请求的对应的服务信息,在这个服务信息里面有个Lease的对象,这个里面就是可以进行心跳续约操作的,更新Lease对象里面的LastUpdateTimestamp时间戳,每一次接收到都会更新这个时间戳的。
4.4、Eureka Server查看心跳是否收到
Eureka Server在启动的时候启动的时候会每60秒遍历一次注册表中的信息,然后查看注册表中的信息是否有过期的,如果过期会加入到一个列表里面单独处理。
清理间隔时间配置(单位毫秒,默认是60*1000,在Eureka的server端配置的):
eureka:
server:
eviction-interval-timer-in-ms: 60
4.5、Eureka Client心跳最大等待时间
Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
。 通过下面可以配置等待时间(这个是在Eureka的client端配置的):
eureka:
instance:
lease-expiration-duration-in-seconds: 90
4.6、总结
通过以上了解,我们一共接触了三个时间配置,一个是注册中心的扫描过期时间
,扫描过期时间就好比,租车公司要定期查看哪些车租聘日到期了,需要进行收回。还有一个客户端设置的 租期更新时间间隔
,这个时间就是租户需要隔一段时间告诉租聘公司,我还要继续租这个车,还有一个是租期到期时间
,这个时间就是假如客户端超过这个时间没有给租聘公司发送心跳,那么就认为不租了。
server端:
eureka.server.eviction-interval-timer-in-ms//清理间隔(单位毫秒,默认是60*1000)
client端:
eureka.instance.lease-renewal-interval-in-seconds =10//租期更新时间间隔(默认30秒)
eureka.instance.lease-expiration-duration-in-seconds =30//租期到期时间(默认90秒)
五、Eureka自我保护机制
首先明确观点:Eureka自我保护机制有很多bug,而且中文资料很缺乏,网上普遍是一篇博客抄来抄去的,本人通过案例也确实发现很多bug的地方,如果钻牛角尖的话,可能几天都学不完!
5.1、什么是自我保护机制
保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护
。为了防止EurekaClient可以正常运行,但是 与 EurekaServer网络不畅通情况下(网络延迟等原因),在保护模式开启的情况下,EurekaServer不会立刻将EurekaClient服务剔除。
如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:
这个需要在注册中心配置的!
server:
#关闭自我保护机制,保证不可用服务被及时踢除,默认为true开启的
enable-self-preservation: false
5.2、为什么会出现自我保护机制
一句话:某时刻某一个微服务因为网络原因不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存,属于CAP里面的AP分支。
因为网络通信是可能恢复的,但是Eureka客户端只会在启动时才去服务端注册。如果因为网络的原因而剔除了客户端,将造成客户端无法再注册到服务端。为了避免此问题,Eureka提供了自我保护机制。
CAP即:
- Consistency(一致性)
- Availability(可用性)
- Partition tolerance(分区容忍性)
清理条件:
- Eureka服务端会检查最近15分钟内所有Eureka 实例正常心跳占比(这个15分钟是在源码当中有个每15分钟执行一次的定时任务),如果低于85%就会触发自我保护机制。
- 触发了保护机制,Eureka将暂时把这些失效的服务保护起来,不让其过期,但这些服务也并不是永远不会过期(该现象可能出现在如果网络不通但是EurekaClient未出现岩机)。
- Eureka在启动完成后,每隔60秒会检查一次服务健康状态(这个60秒就是上面提到的Eureka Server查看心跳是否收到默认的配置)
- 如果这些被保护起来失效的服务过一段时间后(默认90秒,这个90秒就是上面提到的心跳最大等待时间)还是没有恢复,就会把这些服务剔除。如果在此期间服务恢复了并且实例心跳占比高于85%时,就会自动关闭自我保护机制。
- 此时如果换做别的注册中心如果一定时间内没有收到心跳会将剔除该服务,这样就出现了严重失误,因为客户端还能正常发送心跳,只是网络延迟问题,而保护机制是为了解决此问题而产生的。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着
综上,自我保护模式是一种
应对网络异常的安全保护措施
。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
5.3、如何选择关闭还是开启自我保护机制
Eureka服务端默认情况下是会开启自我保护机制的。但我们在不同环境应该选择是否开启保护机制。
自我保护机制的配置是(默认是开启的):eureka.server.enable-self-preservation=true
一般情况下,我们会选择在 开发环境下关闭自我保护机制,而在生产环境下启动自我保护机制。
开发环境下,我们我们启动的服务数量较少而且会经常修改重启。如果开启自我保护机制,很容易触发Eureka客户端心跳占比低于85%的情况。使得Eureka不会剔除我们的服务,从而在我们访问的时候,会访问到可能已经失效的服务,导致请求失败,影响我们的开发。
在生产环境下,我们启动的服务多且不会反复启动修改。环境也相对稳定,影响服务正常运行的人为情况较少。适合开启自我保护机制,让Eureka进行管理。
5.4、Eureka控制台参数
如下我启动了3个服务:
Lease expiration enabled:Eureka自动保护机制启动后该值为false。
Renews threshold:Eureka Server 期望每分钟收到客户端实例续约的阅值。
Renews threshold = 服务实例总数 *(60/续约间隔)*自我保护续约百分比阈值因子(阈值因子默认85%,续约间隔默认是30)。
Renews (last min): Eureka Server 最后1分钟收到客户端实例续约的总数。
Renews(last min) = 服务实例总数 * (60/续约间隔)
如果自我保护模式开启了,且当续约阈值>0,上一分钟的续约数>阈值,那么可以清理。言外之意就是,当上一分钟续约数<阈值,那么就不清理(保护了)
(Renews (last min) < Renews threshold)
5.5、测试开启了自我保护机制
什么都没配置,只是启动了三个服务,然后等了一会就进入保护模式了。对于这一点我也不是很理解。进入保护模式后,我随便关掉了一个服务,Eureka控制台上也就几秒钟立马就消失了,并没有向上面所说的,进入保护模式后,不会轻易移除服务!
我开启了自我保护机制,并且给每个要注册的服务设置如下配置:
eureka:
instance:
#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
lease-expiration-duration-in-seconds: 2
#Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
lease-renewal-interval-in-seconds: 1
注意要观察Renews (last min)和Renews threshold值的时候,启动项目后一定要等个1分钟才可以,等一分钟过后如下:
Renews threshold完全跟上面提到的算法是不一致的,而且差距还很大,而Renews (last min)却差距不是很大。针对于此问题,我也搜了很多相关资料,由于Eureka已经停更,很多人已经弃用,并没有太多的人关注这个问题了。
当我关闭8001服务的时候,Eureka上的8001服务并没有立马消失,而是停留了几秒钟,状态显示的是DOWN。
5.6、关闭自我保护机制
eureka:
server:
enable-self-preservation: false
关闭后,启动注册中心就会看到以下字样!
六、Eureka健康检查
由于server和client通过心跳保持服务状态,而只有状态为UP的服务才能被访问 看eureka界面中的status
比如心跳一直正常,服务一直UP,但是此服务DB连不上了,无法正常提供服务
我们需要将微服务的健康状态也同步到server只需要启动eureka的健康检查这样微服务就会将自己的健康状态同步到eureka
这个需要在Eureka client端设置,而且默认就是开启的。
eureka:
client:
healthcheck:
enabled: true
刚开始我以为是只要这个设置true,然后当服务的mysql挂掉后,会自动更新注册中心的状态,然后可以避免其他服务调用异常的服务,然而并不是,他还需要配置很多东西,并没有我想象的那么神奇!
到此这篇关于关于Eureka的概念作用以及用法详解的文章就介绍到这了,更多相关Eureka概念作用以及用法内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!