一、配置 actuator :
作用:用于监控 springboot 应用,比如:查看状态、健康检查等;
Eureka Servier 项目引入以下依赖即可:
【引入 actuator 依赖:】
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
未配置 actuator (未引入依赖)时,出现以下错误:
配置 actuator 后,再次访问,如下图所示:
对于 /health ,可以在对应项目的配置文件中【application.properties】 中添加:
# 显示详细的健康信息
management.endpoint.health.show-details=always
来展示 /health 的详细信息;
二、服务发现:
对于注册进 注册中心 的服务,可以通过 服务发现 来获取 服务列表的信息;
以 eureka_client_producer_8001 为例,在其中添加一个接口,用于返回 服务信息;
在启动类上添加 @EnableDiscoveryClient 注解 (不添加也可以获取服务信息)
package com.example.eureka_client_producer_8001.Controller;
import com.example.common.Entity.User;
import com.example.common.tools.Result;
import com.example.eureka_client_producer_8001.Service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/producer/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private DiscoveryClient discoveryClient;
@PostMapping("/getDiscovery")
public Result discovery() {
// 获取服务名列表
List<String> serviceList = discoveryClient.getServices();
// 根据服务名 获取 每个服务名下的 各个服务的信息
Map<String, List<ServiceInstance>> map = new HashMap<>();
serviceList.stream().forEach(serviceInstance -> {
map.put(serviceInstance, discoveryClient.getInstances(serviceInstance));
});
// 获取服务名列表
return Result.ok(true, 200, "discovery services success").data("services", map);
// return Result.ok(true, 200, "discovery services success").data("services", userService.discovery());
}
@GetMapping("/getById")
public Result getUser(@RequestParam(value = "id") final Integer id) {
User user = userService.getById(id);
if (user == null) {
return Result.error(false, 404, "数据查询失败");
}
return Result.ok(true, 200, "查询成功").data("user", user);
}
@PostMapping("createUser")
public Result createUser(@RequestBody User user) {
boolean result = userService.save(user);
if (!result) {
return Result.error(false, 404, "数据保存失败");
}
return Result.ok(true, 200, "数据保存成功");
}
}
注册中心上的服务有:
返回值为:
{
"success": true,
"code": 200,
"message": "discovery services success",
"data": {
"services": {
"eureka-producer-8001": [
{
"metadata": {
"management.port": "8001"
},
"secure": false,
"uri": "http://localhost:8001",
"instanceId": "eureka-producer-8001",
"serviceId": "EUREKA-PRODUCER-8001",
"instanceInfo": {
"instanceId": "eureka-producer-8001",
"app": "EUREKA-PRODUCER-8001",
"appGroupName": null,
"ipAddr": "10.8.201.9",
"sid": "na",
"homePageUrl": "http://localhost:8001/",
"statusPageUrl": "http://localhost:8001/actuator/info",
"healthCheckUrl": "http://localhost:8001/actuator/health",
"secureHealthCheckUrl": null,
"vipAddress": "eureka-producer-8001",
"secureVipAddress": "eureka-producer-8001",
"countryId": 1,
"dataCenterInfo": {
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
"name": "MyOwn"
},
"hostName": "localhost",
"status": "UP",
"overriddenStatus": "UNKNOWN",
"leaseInfo": {
"renewalIntervalInSecs": 30,
"durationInSecs": 90,
"registrationTimestamp": 1655342003953,
"lastRenewalTimestamp": 1655342003953,
"evictionTimestamp": 0,
"serviceUpTimestamp": 1655342003954
},
"isCoordinatingDiscoveryServer": false,
"metadata": {
"management.port": "8001"
},
"lastUpdatedTimestamp": 1655342003954,
"lastDirtyTimestamp": 1655342003813,
"actionType": "ADDED",
"asgName": null
},
"scheme": "http",
"host": "localhost",
"port": 8001
}
],
"eureka-client-consumer-9001": []
}
}
}
三、自我保护机制:
【自我保护机制:】
自我保护机制主要用于 Eureka Client 与 Eureka Server 之间存在 网络分区(中断了连接)时 对服务注册表信息的保护。
当自我保护机制开启时,Eureka Server 不再删除 服务注册表中的数据,即不会注销、剔除 任何服务(即使 Eureka Client 宕机了)。
注:
Eureka Server 出现如下提示时,即表示进入了保护模式。
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.
【为什么产生 自我保护机制:】
自我保护机制属于 CAP 原则里的 AP(即在 网络分区时,保证服务的可用性)。
一般情况下,Eureka Server 在一定时间内没有收到 某个服务的心跳(默认 30 秒发一次心跳),Eureka Server 将会注销该实例(默认 90 秒收不到心跳就剔除)。
但是存在特殊情况:发生网络分区故障(比如:延时、拥堵、卡顿)等情况时,服务 与 Eureka Server 之间无法正常通信,此时若直接 剔除服务,那就可能造成很大的影响(此时的服务 本身并没有问题,注销服务 是不合理的)。
为了解决上面的特殊情况,引入了 自我保护 的概念,当 Eureka Server 短时间内丢失过多服务时,将会开启自我保护模式。
自我保护模式一旦开启,将不会注销任何服务实例(宁愿保留错误的服务信息,也不删除正常的服务)。
而自我保护模式一开,客户端访问时就容易访问到 已经不存在的服务信息,将会出现服务调用失败的情况,所以客户端必须进行容错处理(比如:请求重试、断路器等)。
【自我保护机制触发条件:】
经过一分钟,Renews(last min) < Renews threshold * 0.85,就会触发自我保护机制。
注:
Renews(last min) 表示 Eureka 最后一分钟接收的心跳数。
Renews threshold 表示 Eureka 最后一分钟应该接收的心跳数。
开启自我保护模式:
关闭自我保护模式:
【举例:】
以 eureka_server_7000、eureka_client_producer_8001、eureka_client_consumer_9001 为例。
eureka_server_7000 为 Eureka Server。
eureka_client_producer_8001 为 Eureka Client。
eureka_client_consumer_9001 为 Eureka Client。
当 eureka_server_7000 在一定时间内没有接收到 eureka_client_producer_8001 的心跳,将会从服务列表中 剔除 eureka_client_producer_8001 服务。
当 eureka_server_7000 在一定时间内没有接收到 eureka_client_consumer_9001 的心跳,将会从服务列表中 剔除 eureka_client_consumer_9001 服务。
在 Eureka Server 端配置 关闭自我保护模式。
eureka:
server:
enable-self-preservation: false # 关闭自我保护模式
eviction-interval-timer-in-ms: 2000 # 清理无效服务的间隔
在 Eureka Client 端配置 心跳发送时间间隔、以及超时等待时间。
eureka:
instance:
lease-renewal-interval-in-seconds: 1 # 客户端向 注册中心 发送心跳的时间间隔,默认 30 秒
lease-expiration-duration-in-seconds: 5 # 注册中心 等待心跳最长时间,超时剔除服务,默认 90 秒
【在 eureka_server_7000 中 配置关闭自我保护模式:】
server.port=7000
# 配置当前的实例的主机名
eureka.instance.hostname=localhost
# 设置当前实例的IP地址
eureka.instance..ip-address=127.0.0.1
# 设置服务端实例名称,优先级高于 spring.application.name
eureka.instance.appname=Eureka-Server
# 设置实例 ID
eureka.instance.instance-id=eureka-server-instance01
# Eureka Server 自身作为注册中心时,没必要 把自己也注册进 注册中心 (无意义),所以一般下列两项都设置为false
# 默认为 true,设置 false 表示不向注册中心注册自己
eureka.client.registerWithEureka=false
# 默认为 true,设置 false 表示不去注册中心 获取 注册信息
eureka.client.fetchRegistry=false
# 默认为 true,设置 false 表示关闭自我保护模式(Eureka Server 短时间内丢失客户端时,自我保护模式 使 Server 不删除失去连接的客户端)
eureka.server.enable-self-preservation=false
# 清理无效服务的间隔
eureka.server.eviction-interval-timer-in-ms=2000
# 默认为 false,设置为 true 时,则显示在注册中心的是 IP地址, 而非 主机名
#eureka.instance.prefer-ip-address=true
#eureka.instance.health-check-url=http://${spriing.cloud.client.ipAddress}.${server.port}${server.context-path}/health
#eureka.instance.status-page-url=http://${spriing.cloud.client.ipAddress}.${server.port}${server.context-path}/info
#设置 Eureka 服务器地址,类型为 HashMap,默认为: serviceUrl.put("defaultZone", "http://http://localhost:8761/eureka/");
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka
【在 eureka_client_producer_8001 中配置 心跳发送时间间隔、以及超时等待时间:】
server.port=8001
spring.application.name=eureka-producer-8001
#修改点
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
#spring.datasource.data-username=root
spring.datasource.username=root
#spring.datasource.data-password=123456
spring.datasource.password=123456
#修改点
spring.datasource.url=jdbc:mysql://localhost:3306/testMyBatisPlus?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# 配置当前的实例的主机名
eureka.instance.hostname=eureka-client-producer-8001
# 配置服务端实例名称,优先级高于 spring.application.name
eureka.instance.appname=eureka-client-producer
# 设置当前实例 id
eureka.instance.instance-id=eureka-client-producer.instance1
# 客户端向 注册中心 发送心跳的时间间隔,默认 30 秒
eureka.instance.lease-renewal-interval-in-seconds= 1
# 注册中心 等待心跳最长时间,超时剔除服务,默认 90 秒
eureka.instance.lease-expiration-duration-in-seconds=5
# 默认为 true,注册到注册中心
eureka.client.register-with-eureka=true
# 默认为 true,从注册中心 获取 注册信息
eureka.client.fetch-registry=true
# 指向 注册中心 地址,即 eureka_server_7000 的堵住
eureka.client.service-url.defaultZone=http://localhost:7000/eureka
配置运行 三个项目后,进入注册中心:
取消 eureka-client-producer-8001、eureka-client-consumer-9001 的运行,进入注册中心并刷新: