创建父项目

采用Maven的多模块父子聚合项目的方式,首先创建父项目
在父项目的dependencyManagement中子模块所用到的依赖的版本号进行统一管理

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  3. <modelVersion>4.0.0</modelVersion>
  4. <groupId>org.example</groupId>
  5. <artifactId>springcloud</artifactId>
  6. <version>${project.version}</version>
  7. <packaging>pom</packaging>
  8. <properties>
  9. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  10. <maven.compiler.source>1.8</maven.compiler.source>
  11. <maven.compiler.target>1.8</maven.compiler.target>
  12. <junit.version>4.12</junit.version>
  13. <log4j.version>1.2.17</log4j.version>
  14. <lombok.version>1.18.24</lombok.version>
  15. <mysql.version>5.1.47</mysql.version>
  16. <project.version>1.0-SNAPSHOT</project.version>
  17. <druid.version>1.1.16</druid.version>
  18. <mybatis.spring.boot.version>1.3.2</mybatis.spring.boot.version>
  19. </properties>
  20. <dependencyManagement>
  21. <dependencies>
  22. <dependency>
  23. <groupId>org.springframework.boot</groupId>
  24. <artifactId>spring-boot-dependencies</artifactId>
  25. <version>2.2.2.RELEASE</version>
  26. <type>pom</type>
  27. <scope>import</scope>
  28. </dependency>
  29. <dependency>
  30. <groupId>org.springframework.cloud</groupId>
  31. <artifactId>spring-cloud-dependencies</artifactId>
  32. <version>Hoxton.SR1</version>
  33. <type>pom</type>
  34. <scope>import</scope>
  35. </dependency>
  36. <dependency>
  37. <groupId>com.alibaba.cloud</groupId>
  38. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  39. <version>2.1.0.RELEASE</version>
  40. <type>pom</type>
  41. <scope>import</scope>
  42. </dependency>
  43. <dependency>
  44. <groupId>mysql</groupId>
  45. <artifactId>mysql-connector-java</artifactId>
  46. <version>${mysql.version}</version>
  47. </dependency>
  48. <dependency>
  49. <groupId>log4j</groupId>
  50. <artifactId>log4j</artifactId>
  51. <version>${log4j.version}</version>
  52. </dependency>
  53. <dependency>
  54. <groupId>org.projectlombok</groupId>
  55. <artifactId>lombok</artifactId>
  56. <version>${lombok.version}</version>
  57. <optional>true</optional>
  58. </dependency>
  59. <dependency>
  60. <groupId>com.alibaba</groupId>
  61. <artifactId>druid</artifactId>
  62. <version>${druid.version}</version>
  63. </dependency>
  64. <dependency>
  65. <groupId>org.mybatis.spring.boot</groupId>
  66. <artifactId>mybatis-spring-boot-starter</artifactId>
  67. <version>${mybatis.spring.boot.version}</version>
  68. </dependency>
  69. <dependency>
  70. <groupId>junit</groupId>
  71. <artifactId>junit</artifactId>
  72. <version>${junit.version}</version>
  73. <scope>test</scope>
  74. </dependency>
  75. </dependencies>
  76. </dependencyManagement>
  77. <build>
  78. <plugins>
  79. <plugin>
  80. <groupId>org.springframework.boot</groupId>
  81. <artifactId>spring-boot-maven-plugin</artifactId>
  82. <configuration>
  83. <fork>true</fork>
  84. <addResources>true</addResources>
  85. </configuration>
  86. </plugin>
  87. </plugins>
  88. </build>
  89. </project>

创建支付模块

  1. CREATE TABLE `payment`(
  2. `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  3. `serial` varchar(200) DEFAULT '',
  4. PRIMARY KEY (id)
  5. )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4

创建微服务模块按如下步骤进行:

  1. 建Module
  2. 改Pom
  3. 写Yml
  4. 主启动类
  5. 业务类

建Module

创建cloud-provider-payment8001微服务提供者支付Module模块:

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  3. <parent>
  4. <artifactId>springcloud</artifactId>
  5. <groupId>org.example</groupId>
  6. <version>1.0-SNAPSHOT</version>
  7. </parent>
  8. <modelVersion>4.0.0</modelVersion>
  9. <artifactId>cloud-provider-payment8001</artifactId>
  10. <dependencies>
  11. <!--eureka-client-->
  12. <dependency>
  13. <groupId>org.springframework.cloud</groupId>
  14. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.springframework.boot</groupId>
  18. <artifactId>spring-boot-starter-web</artifactId>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-actuator</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>org.mybatis.spring.boot</groupId>
  26. <artifactId>mybatis-spring-boot-starter</artifactId>
  27. </dependency>
  28. <dependency>
  29. <groupId>com.alibaba</groupId>
  30. <artifactId>druid-spring-boot-starter</artifactId>
  31. <version>1.2.11</version>
  32. </dependency>
  33. <!--mysql-connector-java-->
  34. <dependency>
  35. <groupId>mysql</groupId>
  36. <artifactId>mysql-connector-java</artifactId>
  37. </dependency>
  38. <!--jdbc-->
  39. <dependency>
  40. <groupId>org.springframework.boot</groupId>
  41. <artifactId>spring-boot-starter-jdbc</artifactId>
  42. </dependency>
  43. <dependency>
  44. <groupId>org.springframework.boot</groupId>
  45. <artifactId>spring-boot-devtools</artifactId>
  46. <scope>runtime</scope>
  47. <optional>true</optional>
  48. </dependency>
  49. <dependency>
  50. <groupId>org.projectlombok</groupId>
  51. <artifactId>lombok</artifactId>
  52. <optional>true</optional>
  53. </dependency>
  54. <dependency>
  55. <groupId>org.springframework.boot</groupId>
  56. <artifactId>spring-boot-starter-test</artifactId>
  57. <scope>test</scope>
  58. </dependency>
  59. </dependencies>
  60. </project>

写yml

  1. server:
  2. port: 8001
  3. spring:
  4. application:
  5. name: cloud-payment-service
  6. datasource:
  7. type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
  8. driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
  9. url: jdbc:mysql://localhost:3306/cloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
  10. username: root
  11. password: 123456
  12. mybatis:
  13. mapperLocations: classpath:mapper/*.xml
  14. type-aliases-package: org.example.springcloud.entities # 所有Entity别名类所在包

主启动类

  1. @SpringBootApplication
  2. @MapperScan("org.example.springcloud.dao")
  3. public class PaymentApplication8001 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(PaymentApplication8001.class, args);
  6. }
  7. }

业务代码

实体类Payment

  1. import lombok.AllArgsConstructor;
  2. import lombok.Data;
  3. import lombok.NoArgsConstructor;
  4. import java.io.Serializable;
  5. @Data
  6. @AllArgsConstructor
  7. @NoArgsConstructor
  8. public class Payment implements Serializable {
  9. private Long id;
  10. private String serial;
  11. }

…省略增删改查的业务代码
最后得到API接口:

  1. @RestController
  2. @RequestMapping("/payment")
  3. @RequiredArgsConstructor
  4. @Slf4j
  5. public class PaymentController {
  6. final PaymentService paymentService;
  7. @Value("${server.port}")
  8. private String serverPort;
  9. @PostMapping("/create")
  10. public ApiResult create(@RequestBody Payment payment) {
  11. int age = 20;
  12. int result = paymentService.create(payment);
  13. log.info("插入结果:" + result);
  14. if (result > 0) {
  15. return ApiResult.success("插入成功,端口号:" + serverPort);
  16. } else {
  17. return ApiResult.error("插入失败,端口号:" + serverPort);
  18. }
  19. }
  20. @GetMapping("/get/one/{id}")
  21. public ApiResult getPaymentById(@PathVariable("id") Long id) {
  22. Payment payment = paymentService.getPaymentById(id);
  23. log.info("端口号:" + serverPort);
  24. if (payment != null) {
  25. return ApiResult.success(payment);
  26. } else {
  27. return ApiResult.error("未找到对应记录");
  28. }
  29. }
  30. }

创建消费者模块

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  3. <parent>
  4. <artifactId>springcloud</artifactId>
  5. <groupId>org.example</groupId>
  6. <version>1.0-SNAPSHOT</version>
  7. </parent>
  8. <modelVersion>4.0.0</modelVersion>
  9. <artifactId>cloud-consumer-order80</artifactId>
  10. <properties>
  11. </properties>
  12. <dependencies>
  13. <dependency>
  14. <groupId>org.example</groupId>
  15. <artifactId>cloud-api-commons</artifactId>
  16. <version>${project.version}</version>
  17. </dependency>
  18. <dependency>
  19. <groupId>org.springframework.cloud</groupId>
  20. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.springframework.boot</groupId>
  24. <artifactId>spring-boot-starter-web</artifactId>
  25. </dependency>
  26. <dependency>
  27. <groupId>org.springframework.boot</groupId>
  28. <artifactId>spring-boot-starter-actuator</artifactId>
  29. </dependency>
  30. <dependency>
  31. <groupId>org.springframework.boot</groupId>
  32. <artifactId>spring-boot-devtools</artifactId>
  33. <scope>runtime</scope>
  34. <optional>true</optional>
  35. </dependency>
  36. <dependency>
  37. <groupId>org.projectlombok</groupId>
  38. <artifactId>lombok</artifactId>
  39. <optional>true</optional>
  40. </dependency>
  41. <dependency>
  42. <groupId>org.springframework.boot</groupId>
  43. <artifactId>spring-boot-starter-test</artifactId>
  44. <scope>test</scope>
  45. </dependency>
  46. </dependencies>
  47. </project>

… 省略部分操作

RestTemplate

由于消费者订单模块和支付模块之间是不同的服务,而在微服务架构中,不同的服务之间采用的是HTTP请求调用的方式来进行通信。
而Spring为我们封装了进行远程调用的类RestTemplate
我们只需要将此类配置为Bean并注入到我们的容器中即可

  1. @Configuration
  2. public class ApplicationContextConfig {
  3. @Bean
  4. @LoadBalanced
  5. public RestTemplate restTemplate() {
  6. return new RestTemplate();
  7. }
  8. }

controller

  1. @Slf4j
  2. @RestController
  3. public class OrderController {
  4. public static final String PAYMENT_URL = "http://localhost:8001";
  5. @Resource
  6. private RestTemplate restTemplate;
  7. @GetMapping("/consumer/payment/create")
  8. public CommonResult<Payment> create(Payment payment){
  9. return restTemplate.postForObject(PAYMENT_URL+"/payment/create", payment, CommonResult.class);
  10. }
  11. @GetMapping("/consumer/payment/get/{id}")
  12. public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
  13. return restTemplate.getForObject(PAYMENT_URL+"/payment/get/one/"+id, CommonResult.class);
  14. }
  15. }

这样就可以在消费者模块中通过远程调用支付模块
image.png

优化模块

在支付模块和消费者模块中重复使用到了Payment实体和统一返回结果ApiResult
因此我们可以将一些公共的API或者实体类放在一个单独的模块中,然后在需要的模块中进行引用。

  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  3. <parent>
  4. <artifactId>springcloud</artifactId>
  5. <groupId>org.example</groupId>
  6. <version>1.0-SNAPSHOT</version>
  7. </parent>
  8. <modelVersion>4.0.0</modelVersion>
  9. <artifactId>cloud-api-commons</artifactId>
  10. <dependencies>
  11. <dependency>
  12. <groupId>org.springframework.boot</groupId>
  13. <artifactId>spring-boot-devtools</artifactId>
  14. <scope>runtime</scope>
  15. <optional>true</optional>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.projectlombok</groupId>
  19. <artifactId>lombok</artifactId>
  20. <optional>true</optional>
  21. </dependency>
  22. <dependency>
  23. <groupId>cn.hutool</groupId>
  24. <artifactId>hutool-all</artifactId>
  25. <version>5.8.3</version>
  26. </dependency>
  27. </dependencies>
  28. </project>

image.png

在其他需要使用到这个模块的微服务中添加依赖,从而进行使用
image.png

注册中心

Eureka

SpringCloud封装了Netflix公司开发的Eureka模块来实现服务治理

在传统的RPC远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
image.png
在上述中,我们已经实现了不同的服务之间通过http请求的方式进行通信
但是实际生产环境的部署过程中,我们一台微服务可能会进行集群部署,那么将请求的URL地址固定写死,显然是不合适的,因此我们需要使用到一个中间层:注册中心来帮助我们进行服务之间的管理

当服务器启动的时候,会把当前自己服务器的信息比如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地进行RPC远程调用

Eureka分为Server和Client

顾名思义,一个是服务端,一个是客户端

  • 各个微服务节点通过配置启动之后,会在EurekaServer中进行注册,这样EurekaServer服务注册表中就会存储所有可用节点的信息,服务节点的信息可以直接在界面中直观的看到
  • EurekaClient是运行在服务节点中的,在应用启动之后,会向Server发送心跳,从而确保自身的可用性。

创建EurekaServer模块

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>springcloud</artifactId>
  7. <groupId>org.example</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>cloud-eureka-server7001</artifactId>
  12. <properties>
  13. <maven.compiler.source>8</maven.compiler.source>
  14. <maven.compiler.target>8</maven.compiler.target>
  15. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  16. </properties>
  17. <dependencies>
  18. <!--eureka-server-->
  19. <dependency>
  20. <groupId>org.springframework.cloud</groupId>
  21. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  22. </dependency>
  23. <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
  24. <dependency>
  25. <groupId>org.example</groupId>
  26. <artifactId>cloud-api-commons</artifactId>
  27. <version>${project.version}</version>
  28. </dependency>
  29. <!--boot web actuator-->
  30. <dependency>
  31. <groupId>org.springframework.boot</groupId>
  32. <artifactId>spring-boot-starter-web</artifactId>
  33. </dependency>
  34. <dependency>
  35. <groupId>org.springframework.boot</groupId>
  36. <artifactId>spring-boot-starter-actuator</artifactId>
  37. </dependency>
  38. <!--一般通用配置-->
  39. <dependency>
  40. <groupId>org.springframework.boot</groupId>
  41. <artifactId>spring-boot-devtools</artifactId>
  42. <scope>runtime</scope>
  43. <optional>true</optional>
  44. </dependency>
  45. <dependency>
  46. <groupId>org.projectlombok</groupId>
  47. <artifactId>lombok</artifactId>
  48. </dependency>
  49. <dependency>
  50. <groupId>org.springframework.boot</groupId>
  51. <artifactId>spring-boot-starter-test</artifactId>
  52. <scope>test</scope>
  53. </dependency>
  54. <dependency>
  55. <groupId>junit</groupId>
  56. <artifactId>junit</artifactId>
  57. </dependency>
  58. </dependencies>
  59. </project>

配置文件:

  1. server:
  2. port: 7001
  3. eureka:
  4. instance:
  5. hostname: locathost #eureka服务端的实例名称
  6. client:
  7. #false表示不向注册中心注册自己。
  8. register-with-eureka: false
  9. #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
  10. fetch-registry: false
  11. service-url:
  12. #设置与Eureka server交互的地址查询服务和注册服务都需要依赖这个地址。
  13. defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

主启动类添加注解@EnableEurekaServer

  1. import org.springframework.boot.SpringApplication;
  2. import org.springframework.boot.autoconfigure.SpringBootApplication;
  3. import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
  4. @SpringBootApplication
  5. @EnableEurekaServer
  6. public class EurekaMain7001 {
  7. public static void main(String[] args) {
  8. SpringApplication.run(EurekaMain7001.class, args);
  9. }
  10. }

运行Server模块,访问http://localhost:70001/,可以看到Spring Eureka服务主页。

支付模块和消费者模块接入EurekaServer

在两个模块中都添加依赖

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  4. </dependency>

修改配置文件:

  1. eureka:
  2. client:
  3. #表示是否将自己注册进Eurekaserver默认为true。
  4. register-with-eureka: true
  5. #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
  6. fetchRegistry: true
  7. service-url:
  8. defaultZone: http://localhost:7001/eureka

在启动类上添加@EnableEurekaClient注解
image.png
Eureka的可视化页面中可以看到两个服务已经成功注册进来

image.png

EurekaServer集群环境构建

再创建一个Server模块,并修改本地hosts文件,添加如下配置:

  1. 127.0.0.1 eureka7001.com
  2. 127.0.0.1 eureka7002.com

修改server7001的配置文件

  1. server:
  2. port: 7001
  3. spring:
  4. application:
  5. name: spring-cloud-eureka
  6. eureka:
  7. instance:
  8. hostname: eureka7001
  9. client:
  10. register-with-eureka: false
  11. fetch-registry: false
  12. service-url:
  13. # 集群部署的时候指向其他eureka
  14. defaultZone: http://eureka7002.com:7002/eureka/
  15. # defaultZone: http://eureka7001.com:7001/eureka/

修改server7002的配置文件

  1. server:
  2. port: 7002
  3. eureka:
  4. instance:
  5. hostname: eureka7002.com
  6. client:
  7. register-with-eureka: false
  8. fetch-registry: false
  9. service-url:
  10. # defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  11. defaultZone: http://eureka7001.com:7001/eureka/
  12. spring:
  13. application:
  14. name: spring-cloud-eureka

两个微服务注册进集群

  1. eureka:
  2. client:
  3. register-with-eureka: true
  4. fetch-registry: true
  5. service-url:
  6. defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/

image.png

支付模块集群

新建模块cloud-provider-payment8002
并修改Controller,将端口值进行返回,从而判断具体是哪个服务进行了响应
由于我们现在有两个支付模块
因此我们在消费模块中具体请求的是哪个服务应该是动态决定的,而不是固定写死的
使用@LoadBalanced注解赋予RestTemplate负载均衡的能力

  1. import org.springframework.cloud.client.loadbalancer.LoadBalanced;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.client.RestTemplate;
  5. @Configuration
  6. public class ApplicationContextConfig {
  7. @Bean
  8. @LoadBalanced//使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
  9. public RestTemplate getRestTemplate(){
  10. return new RestTemplate();
  11. }
  12. }

并且将我们请求的url修改为服务名
image.png

  1. public static final String PAYMENT_URL =
  2. "http://CLOUD-PAYMENT-SERVICE";
  1. 先启动两个注册中心,组成集群
  2. 然后启动支付模块,注册进注册中心,并组成集群
  3. 最后启动消费模块,调用支付模块提供的服务

image.png

服务发现Discovery

对于注册进Eureka里面的服务,可以通过服务发现来获取服务的信息
在需要获取服务信息的地方注入DiscoveryClient

  1. @RestController
  2. @Slf4j
  3. public class PaymentController{
  4. ...
  5. @Resource
  6. private DiscoveryClient discoveryClient;
  7. ...
  8. @GetMapping(value = "/payment/discovery")
  9. public Object discovery()
  10. {
  11. List<String> services = discoveryClient.getServices();
  12. for (String element : services) {
  13. log.info("*****element: "+element);
  14. }
  15. List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
  16. for (ServiceInstance instance : instances) {
  17. log.info(instance.getServiceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
  18. }
  19. return this.discoveryClient;
  20. }
  21. }

然后在启动类上添加上注解:@EnableDiscoveryClient

Eureka自我保护理论

如果在Euraka的首页看到下面这些红字,那就说明进入了自我保护的模式
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THANTHRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUSTTO BE SAFE
导致原因
在Eureka中,如果某个时刻注册进Eureka的服务不可用了,Eureka不会立即进行清理,而是依然会对该微服务的信息进行保存
为什么会产生Eureka自我保护机制
为了保证EurekaClient可以正常运行,防止在一段时间内与EurekaServer之间网络不同的时候,EurekaServer直接将其清除而带来的低可用性问题
什么是自我保护机制
默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
关闭自我保护机制
image.png

Zookeeper

Zookeeper作为一个分布式协调工具,同样实现注册中心的功能
Spring Cloud 学习笔记(1 / 3)_巨輪的博客-CSDN博客