微服务+SpringCloud(7.30)

一、单体、分布式、集群

1、单体架构

定义:所有业务代码都打包在一个项目里,部署在一台服务器上。

特点:开发、部署简单,适合初期项目。但存在单点故障风险,且受限于单机硬件性能,无法应对高并发。

image-20260730165046801

2、分布式架构

定义:将不同的业务组件(如 Tomcat 和 MySQL)拆分部署在不同的服务器上,协同完成请求。

特点:通过“分工”解决单机资源瓶颈,提升系统整体处理能力。

image-20260730165332375

3、集群架构

定义:将同一个业务模块复制部署在多台服务器上,形成“节点”集群。

特点:通过“复制”解决单点故障,成倍提升系统性能。通常需要配合负载均衡服务器,将请求均匀分发给负载较小的节点。

image-20260730170200837

二、系统架构演变

演变过程:单体应用架构--->垂直应用架构--->分布式架构--->SOA架构--->微服务架构。

1、单体应用架构

定义:将所有功能模块(如用户、商品、订单、物流等)集成在一个 Web 项目中,部署在一台 Tomcat 服务器上。

优点:项目架构简单,开发、部署和维护成本低,适合小型项目。

缺点:全部功能耦合在一个工程中,大型项目不易开发维护;模块紧密耦合,单点容错率低;无法针对不同模块进行针对性优化和水平扩展。

image-20260730170631211

2、垂直应用架构

定义:随着访问量增大,将原来的单体应用拆分成互不相干的几个应用(如电商系统、后台系统、CMS系统)。

优点:实现了流量分担,解决了并发问题;可以针对不同模块进行优化和水平扩展(如只增加订单模块节点);一个系统的问题不会影响其他系统,提高了容错率。

缺点:系统之间相互独立,无法进行相互调用;存在重复的开发任务。

image-20260730171058172

3、分布式架构

定义:将垂直应用中重复的业务代码抽取出来,做成统一的业务层作为独立的服务。工程被拆分成表现层(controller层)和服务层(service、mapper层)。

优点:抽取公共功能为服务层,提高了代码复用性。

缺点:系统间耦合度变高,调用关系错综复杂,难以维护。

image-20260730171628152

4、SOA架构(基于服务的架构)

定义:在分布式架构下,增加一个调度中心(资源调度和治理中心)对集群进行实时管理。

优点:使用注册中心解决了服务间调用关系的自动调节。

缺点:服务间存在依赖关系,一旦某个环节出错会影响较大(服务雪崩);服务关系复杂,运维、测试部署困难。

image-20260730172022541

5、微服务架构

定义:SOA 架构的进一步发展,强调服务的“彻底拆分”。将单体应用进一步拆分成更小的服务,每个服务都是一个可以独立运行的项目。

优点:服务原子化拆分,独立打包、部署和升级,任务划分清晰,利于扩展;微服务之间采用 RESTful 等轻量级 HTTP 协议相互调用。

缺点:分布式系统开发的技术成本高(如容错、分布式事务等)。

image-20260730172209288

三、微服务架构介绍

1. 微服务架构定义

本质:将单体应用进一步拆分为更小的服务,每个服务都是可独立运行的项目。

一旦采用微服务系统架构,就势必会遇到这样几个问题:

  • 这么多小服务,如何管理他们?

  • 这么多小服务,他们之间如何通讯?

  • 这么多小服务,客户端怎么访问他们?

  • 这么多小服务,一旦出现问题了,应该如何自处理?

  • 这么多小服务,一旦出现问题了,应该如何排错?

对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。

image-20260730173511895

2. Spring Cloud 介绍

定位:一系列框架的集合,利用 Spring Boot 简化分布式系统基础设施开发(如服务发现、配置中心、负载均衡等),通过封装成熟框架屏蔽复杂配置,提供易部署维护的工具包。

Spring Boot 与 Spring Cloud 的关系
  • Spring Boot:专注于快速开发单个微服务个体。
  • Spring Cloud:关注全局微服务协调整理治理,整合 Spring Boot 开发的微服务,提供服务发现、断路器、分布式事务等集成服务。
  • 总结:Spring Boot 负责"造零件",Spring Cloud 负责"组装和治理整个机器"。

3. Spring Cloud 版本命名规则

  • 格式:版本名 + 版本号(如 Greenwich.SR6)。
  • 版本名:采用伦敦地铁名称(如 Greenwich)。
  • 版本号:SR(Service Releases)表示稳定版,后接递增数字(如 SR6 为第 6 个稳定版)。

4. 选择 Spring Cloud Alibaba 的原因

  • 背景:Spring Cloud Netflix 的核心组件(Eureka、Hystrix、Zuul)已停止更新,虽可继续使用但无官方维护,问题需自行解决。
  • 优势:Spring Cloud Alibaba 作为替代方案,提供更活跃的社区支持和持续更新的组件,更适合当前分布式系统开发需求。

四、微服务环境搭建

1、 业务场景设计

​ 项目通过两个核心微服务来演示微服务之间的交互:

商品微服务:查询商品列表。

订单微服务:创建订单。

2、模块设计

--- shop-parent 父工程
 --- shop-product-api 商品微服务api 【存放商品实体】
 --- shop-product-server 商品微服务 【端口:808x】
 --- shop-order-api 订单微服务api 【存放订单实体】
 --- shop-order-server 订单微服务 【端口:809x】

3、微服务调用

在微服务架构中,最常见的场景就是微服务之间的相互调用。我们以电商系统中常见的用户下单为例

来演示微服务的调用:客户向订单微服务发起一个下单的请求,在进行保存订单之前需要调用商品微服

务查询商品的信息。

我们一般把服务的主动调用方称为服务消费者,把服务的被调用方称为服务提供者

image-20260730174722553

4、实战练习

(1)首先需要创建订单表(t_shop_order)和产品表(t_shop_product)

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for t_shop_order
-- ----------------------------
DROP TABLE IF EXISTS `t_shop_order`;
CREATE TABLE `t_shop_order` (
  `oid` bigint(20) NOT NULL AUTO_INCREMENT,
  `uid` bigint(20) DEFAULT NULL,
  `username` varchar(255) DEFAULT NULL,
  `pid` bigint(20) DEFAULT NULL,
  `pname` varchar(255) DEFAULT NULL,
  `pprice` double(10,2) DEFAULT NULL,
  `number` int(11) DEFAULT NULL,
  PRIMARY KEY (`oid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_shop_order
-- ----------------------------

-- ----------------------------
-- Table structure for t_shop_product
-- ----------------------------
DROP TABLE IF EXISTS `t_shop_product`;
CREATE TABLE `t_shop_product` (
  `pid` bigint(20) NOT NULL AUTO_INCREMENT,
  `pname` varchar(255) DEFAULT NULL,
  `pprice` double(10,2) DEFAULT NULL,
  `stock` int(11) DEFAULT NULL,
  PRIMARY KEY (`pid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_shop_product
-- ----------------------------
INSERT INTO `t_shop_product` VALUES ('1', '小米', '1000.00', '5000');
INSERT INTO `t_shop_product` VALUES ('2', '华为', '2000.00', '5000');
INSERT INTO `t_shop_product` VALUES ('3', '苹果', '3000.00', '5000');
INSERT INTO `t_shop_product` VALUES ('4', 'OPPO', '4000.00', '5000');

(2)创建父工程

Q1.创建的父项目是SpringBoot项目还是Maven项目还是普通Java项目呢?

答案:父项目是SpringBoot项目,子项目是Maven项目,就可以继承了。

Q2.那如果父项目是普通 Maven,子项目是 Boot可行吗?

答案不可以,子项目不能同时拥有两个父项目。

  • 一个 Spring Boot 子项目,它的 <parent> 必须指向 spring-boot-starter-parent,才能获取 Boot 的核心能力(如依赖版本锁定、打包插件等)。
  • 如果子项目继承了自定义的普通 Maven 父工程(比如 shop-parent),它就无法再直接继承 spring-boot-starter-parent 了。

①首先创建项目,选择SpringBoot项目,选择Maven,打Jar包,最后勾选一个Lombok。

image-20260730182152065

②父项目可可以管理子项目(聚合)、管理依赖版本(继承)、管理公共配置

父项目中的src目录需要删除。

③修改父项目的父工程的版本为2.6.11

<!--父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.11</version>
</parent>

④删除 <build>标签内容

⑤管理依赖版本

    <properties>
        <java.version>17</java.version>
        <spring-cloud.version>2021.0.4</spring-cloud.version>
        <spring-cloud-alibaba.version>2021.0.4.0</spring-cloud-alibaba.version>
    </properties>
 <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

(3)创建shop-product-api项目。父项目右键新建模块,选择Java项目、选择Maven,确认父项。

image-20260730211300535

​ 创建shop-product-server项目。父项目右键新建模块,选择Java项目、选择Maven,确认父项。

(4)在shop-product-serverjava包下创建cn.wolfcod,将MyBatis-Plus提供的代码生成器拷贝到下面。添加完后,在shop-product-server的pom下添加依赖。

  <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

在代码生成器中修改配置。

  • 修改url中的库名为cloud73
  • 修改密码。
  • 修改要生成的的表名为t_shop_product。修改输出路径
 .outputDir(System.getProperty("user.dir") + "/shop-product-server/src/main/java") // 输出路径
package cn.wolfcode;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.Collections;

public class CodeGenerator {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/java-test?useSSL=false&serverTimezone=GMT%2B8";
        String username = "root";
        String password = "123456";
//        String moduleName = "system";
        String[] tableNames = {"employee"}; // 要生成的表名

        FastAutoGenerator.create(url, username, password)
            .globalConfig(builder -> {
                builder.author("y") // 设置作者
                    .outputDir(System.getProperty("user.dir") + "/src/main/java") // 输出路径
                    .enableSwagger() // 开启swagger注解
                    .disableOpenDir(); // 禁止打开输出目录
            })
            .packageConfig(builder -> {
                builder.parent("cn.wolfcode");
            })
            .strategyConfig(builder -> {
                builder.addInclude(tableNames) // 包含的表
                    .entityBuilder() // 实体配置
                    .enableLombok() // 启用Lombok
                    .controllerBuilder() // 控制器配置
                    .enableRestStyle(); // 启用RestController
            })
            .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎
            .execute();
    }
}

(5)点击运行,生成完后在entity中删除@ApiModel(value = "TShopProduct对象", description = "")和爆红import。@Getter@Setter换成@Data

(6)在shop-product-apijava目录下新建cn.wolfcode.entity,将shop-product-server中生成的实体类拷贝到shop-product-api中。在shop-product-api的pom下引入依赖。

<dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-annotation</artifactId>
            <version>3.5.2</version>
        </dependency>
    </dependencies>

​ 删除shop-product-server中的entity包。

(7)点击shop-product-server项目,mapper、server爆红是因为没有实体类,只需在其pom下引入shop-product-api即可。

 <dependency>
            <groupId>cn.wolfcode</groupId>
            <artifactId>shop-product-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

(8)为了启动项目,先在shop-product-server项目的resource下创建application.yml文件。

server:
  port: 8081

spring:
  application:
    name: product
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cloud73?useSSL=false&serverTimezone=UTC
    username: root
    password: 1234
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    

​ 在cn.wolfcdoe下创建启动类ProductApp,就可以测试启动了。

package cn.wolfcode;


import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("cn.wolfcode.mapper")
@SpringBootApplication
public class ProductApp {
    public static void main(String[] args) {
        SpringApplication.run(ProductApp.class, args);
    }
}

(9)为这个程序创建一个接口 ---> 查询商品详情。在shop-product-serverTShopProductController中编写代码。

@RestController
@RequestMapping("/product")
public class TShopProductController {
    @Autowired
    private ITShopProductService productService;

    @GetMapping("/get/{id}")
    public TShopProduct get(@PathVariable("id") Integer id) {

        TShopProduct product = productService.getById(id);
        product.setPname(product.getPname());
        return product;
    }

}

(10)选择用启动参数模拟808180828083

  • 点击,编辑配置。

image-20260730215535252

  • 移除父项目启动类。
  • 修改当前启动类为Product8081
  • 点击 + 号,选择SpringBoot程序,命名为Product8082
    • 选择主类为cn.wolfcode.ProductApp
    • 点击修改选项 ---> 添加虚拟选项。虚拟机选项输入-Dserver.port=8082
  • 同样操作,添加Product8083

注入端口号,查看端口。

@RestController
@RequestMapping("/product")
public class TShopProductController {
    @Autowired
    private ITShopProductService productService;

    @Value("${server.port}")
    private String port;

    @GetMapping("/get/{id}")
    public TShopProduct get(@PathVariable("id") Integer id) {

        TShopProduct product = productService.getById(id);
        product.setPname(product.getPname()+port);
        return product;
    }

}

(11)右上角把三个端口都启动。alt+8服务,添加服务--->运行配置--->SpringBoot。

(12)三个端口都开启后,可以访问不同端口。

localhost:8081/product/get/3

localhost:8082/product/get/3

localhost:8083/product/get/3

(13)按照上面步骤搭建shop-order-apishop-order-server

​ 直接使用在shop-product-server下的CodeGenerator代码生成器,修改表名为t_shop_order,以及修改输出路径为

.outputDir(System.getProperty("user.dir") + "/shop-order-server/src/main/java") // 输出路径

​ 生成后在shop-order-server下的pom添加依赖。

<dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

​ 将生成的实体类中的爆红删除掉,在把@Getter和@Setter删除,添加@Data。

​ 同样将修改好的实体类拷贝到shop-order-api中的cn.wolfcode.entity中,删除shop-order-server中的entity。

​ 在shop-order-api的pom中添加依赖。

 <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-annotation</artifactId>
            <version>3.5.2</version>
        </dependency>
    </dependencies>

​ 在父项目的pom下引入shop-order-api的依赖。也要在中引入shop-order-api的依赖。

​ 在shop-order-serverresource下添加application.yml。改portname

server:
  port: 8091

spring:
  application:
    name: order
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cloud73?useSSL=false&serverTimezone=UTC
    username: root
    password: 1234
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl


​ 在shop-order-server中添加启动类OrderApp

@MapperScan("cn.wolfcode.mapper")
@SpringBootApplication
public class OrderApp {
    public static void main(String[] args) {
        SpringApplication.run(OrderApp.class, args);
    }
}

(14)shop-order-serverTShopOrderController编写接口。

@RestController
@RequestMapping("/order")
public class TShopOrderController {

    @Autowired
    private ITShopOrderService orderService;

    @RequestMapping("/save/{uid}/{pid}/{number}")
    public TShopOrder save(@PathVariable Long uid, @PathVariable Long pid, @PathVariable Integer number) {
        TShopOrder tShopOrder = new TShopOrder();
        tShopOrder.setUid(uid);
        tShopOrder.setPid(pid);
        tShopOrder.setNumber(number);
        orderService.save(tShopOrder);
        return tShopOrder;
    }
    
}

(15)查看t_shop_order表,每次保存pnamepprice为空。我们应该在shop-order-server去访问shop-order-api里的根据id查询,将查回来的商品信息回填到t_shop_order表中。

image-20260730222111719

商品微服务已经提供了数据接口了,订单微服务应该如何去调用呢?

其实就是如何通过Java代码去调用一个http的接口地址,我们可以使用RestTemplate来进行调用.

  • 在启动类上添加RestTemplateBean配置

    @SpringBootApplication
    public class OrderServer {
    public static void main(String[] args) {
    SpringApplication.run(OrderServer.class,args);
    }
    @Bean
    public RestTemplate restTemplate(){
    return new RestTemplate();
    }
    }
    
  • 或者直接new一个RestTemplate对象,但是要考虑释放。

@RestController
@RequestMapping("/order")
public class TShopOrderController {
    @Autowired
    private ITShopOrderService orderService;

    @RequestMapping("/save/{uid}/{pid}/{number}")
    public TShopOrder save(@PathVariable Long uid,  @PathVariable Long pid, @PathVariable Integer number) {
        TShopOrder tShopOrder = new TShopOrder();
        tShopOrder.setUid(uid);
        tShopOrder.setPid(pid);
        tShopOrder.setNumber(number);

        //用于发送restful风格的http请求的对象,在spring-boot-starter-web中提供
        RestTemplate restTemplate = new RestTemplate();
        TShopProduct product = restTemplate.getForObject("http://localhost:8081/product/get/3", TShopProduct.class);
        tShopOrder.setPname(product.getPname());
        tShopOrder.setPprice(product.getPprice());


        orderService.save(tShopOrder);
        return tShopOrder;
    }
}

​ 那我们在shop-order-server中没有shop-product-api,所以需要在shop-order-server中引入shop-product-api的依赖。

    <dependency>
            <groupId>cn.wolfcode</groupId>
            <artifactId>shop-product-api</artifactId>
        </dependency>

(16)上面我们发送请求会一直访问8081,下面就用简单的算法做一下负载均衡。

@RestController
@RequestMapping("/order")
public class TShopOrderController {
    @Autowired
    private ITShopOrderService orderService;

    @RequestMapping("/save/{uid}/{pid}/{number}")
    public TShopOrder save(@PathVariable Long uid,  @PathVariable Long pid, @PathVariable Integer number) {
        TShopOrder tShopOrder = new TShopOrder();
        tShopOrder.setUid(uid);
        tShopOrder.setPid(pid);
        tShopOrder.setNumber(number);

		// 添加服务器
        List<String> urlList = new ArrayList<>();
        urlList.add("localhost:8081");
        urlList.add("localhost:8082");
        urlList.add("localhost:8083");

        //用于发送restful风格的http请求的对象,在spring-boot-starter-web中提供
        RestTemplate restTemplate = new RestTemplate();
        
		// 随机取一个服务器
        Random random = new Random();
        int rand = random.nextInt(urlList.size());
        String url = urlList.get(rand);

        TShopProduct product = restTemplate.getForObject("http://"+url+"/product/get/3", TShopProduct.class);
        tShopOrder.setPname(product.getPname());
        tShopOrder.setPprice(product.getPprice());

        orderService.save(tShopOrder);
        return tShopOrder;
    }
}

​ 虽然我们已经可以实现微服务之间的调用。但是我们把服务提供者的网络地址(ip,端口)等硬编码到了代码中,这种做法存在许多问题:

  • 一旦服务提供者地址变化,就需要手工修改代码
  • 一旦是多个服务提供者,无法实现负载均衡功能
  • 一旦服务变得越来越多,人工维护调用关系困难

那么应该怎么解决呢, 这时候就需要通过注册中心动态的实现服务治理

五、服务治理

服务治理是微服务架构中最核心最基本的模块。用于实现各个微服务的自动化注册与发现

服务注册:服务器在启动时,将自身信息(ip、通讯协议、版本等)上报给注册中心服务器。

  • 注册中心服务器中形成服务器列表。

服务发现:一台服务器发送请求到注册中心,获取另一台服务器的信息(ip、端口、通信协议、版本等),并缓存到本地的过程。

服务心跳:每台服务器每间隔一定时间发送一个心跳数据,证明自己还活着。

  • 一般3~5个周期不发送心跳是没问题的,因为有网络波动等原因,但是一直不发就从服务清单移除该服务器了。

服务健康检查:注册中心服务器,定期检查所有服务,看服务是否发送心跳数据,如果有服务器多个周期没有发送心跳数据,就会从服务清单中移除。

1、Nacos简介

Nacos DiscoverySpring Cloud Alibaba 体系中的服务注册与发现组件,它是微服务架构中服务治理的基础设施。

核心功能点:

  • 服务注册: Nacos Client会通过发送REST请求想Nacos Server注册自己的服务,提供自身的元数据,比如IP地址,端口等信息。Nacos Server接收到注册请求后,就会把这些元数据存储到一个双层的内存Map中。

  • 服务心跳: 在服务注册后,Nacos Client会维护一个定时心跳来维持统治Nacos Server,说明服务一致处于可用状态,防止被剔除,默认5s发送一次心跳

  • 服务同步: Nacos Server集群之间会相互同步服务实例,用来保证服务信息的一致性。

  • 服务发现: 服务消费者(Nacos Client)在调用服务提供的服务时,会发送一个REST请求给Nacos Server,获取上面注册的服务清单,并且缓存在Nacos Client本地,同时会在Nacos Client本地开启一个定时任务拉取服务最新的注册表信息更新到本地缓存。

  • 服务健康检查: Nacos Server 会开启一个定时任务来检查注册服务实例的健康情况,对于超过15s没有收到客户端心跳的实例会将他的healthy属性设置为false(客户端服务发现时不会发现),如果某个实例超过30s没有收到心跳,直接剔除该实例(被剔除的实例如果恢复发送心跳则会重新注册)。

image-20260730231128834

Q1、若服务心跳是5秒一次,注册中心是15秒(三个周期)去检查,当有一台服务器15秒没发心跳,第16秒的时候会不会被注册中心移除?

答案:不一定。

​ 因为健康检查巡逻机制的,要等到检查才能发现。不一定会很长时间检查一次,但间隔时间不会太长。可能15秒没发心跳,但是等到20秒掉了。其实第15秒没发心跳,注册中心应该移除该服务器了,但是在第15秒~20秒期间内,注册中心没去检查到这台服务器没发送心跳,结果就是另一台服务器还能从注册中心获取到该服务器的信息,还可以向该服务器发送请求。结果是大量请求发送到心跳停止的服务器,导致该服务器一直不能反馈,这也会影响发送请求的服务器,导致等不到返回的数据,会堆一堆请求,别人再访问也可能会导致服务器死机。

Q2、当去访问下订单时,一会调用成功一会调用失败,为什么?

答案:商品服务的这三台服务器中,有一台坏掉了。

某一台商品服务器坏掉了之后,注册中心已经发现了,并把该服务器从服务清单中移除了。正常来说我们去访问注册中心发现这台服务器没有,就不应该去访问了,但是我们订单服务中缓存了商品服务的信息。缓存没更新之前,也就是没从注册中心拉取下来之前,订单服务一直缓存了坏了的服务器。负载均衡去访问时,会间歇性调用失败。

六、常见注册中心

1、Zookeeper

Zookeeper是一个分布式服务框架,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。

2、Eureka

Eureka是Springcloud Netflflix中的重要组件,主要作用就是做服务注册和发现。但是现在已经闭源。

3、Consul

Consul是基于GO语言开发的开源工具,主要面向分布式,服务化的系统提供服务注册、服务发现和配置管理的功能。Consul的功能都很实用,其中包括:服务注册/发现、健康检查、Key/Value存储、多数据中心和分布式一致性保证等特性。Consul本身只是一个二进制的可执行文件,所以安装和部署都非常简单,只需要从官网下载后,在执行对应的启动脚本即可。

4、Nacos

Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它是 SpringCloud Alibaba 组件之一,负责服务注册发现和服务配置。

七、高可用和数据一致性

在微服务架构中,高可用数据一致性注册中心设计的两大核心目标,它们共同决定了系统的稳定性和可靠性

1、高可用(AP模式)

为了防止单点故障,注册中心通常采用集群部署

  • 节点平等:集群中的服务器是并列关系,没有主从之分,任何一台都可以处理服务注册和发现的请求。
  • 数据同步:当服务器1注册了A服务,服务器2注册了B服务时,节点间会相互通信(服务器1向服务器2发送A,服务器2向服务器1发送B),并最终将完整数据同步给服务器3。
  • 最终一致性:在数据同步完成前,集群内会存在短暂的数据不一致。但这换取了极高的可用性——即使服务器1宕机,服务器2和3依然能正常提供服务,系统整体不会瘫痪。

image-20260731174642321

2、数据一致性(CP模式)

为了保证数据的绝对准确,注册中心采用主从架构

  • 主从机制:集群中存在唯一的主服务器(Leader),其余为从服务器(Follower)。所有的读写入口都在主服务器,当主服务器注册A服务后,会将信息同步给所有从服务器。因为数据只能在主服务器添加,所以全局数据始终保持一致。
  • 故障影响:从服务器宕机影响较小,仅相当于少了一个备份;但主服务器一旦宕机,整个集群将无法进行任何操作
  • Leader 选举:主服务器宕机后,剩余的从服务器会触发选举机制。它们会相互发送消息,比较性能、地域节点等信息,选出最优节点成为新的主服务器。
  • 可用性牺牲:在选举期间,由于没有主服务器,系统处于不可用状态。此外,网络分区故障不可避免(例如主服务器因网络问题失联5分钟后恢复),这会触发重新选举,导致服务在选举期间中断。

image-20260731180422337

posted on 2026-07-31 18:24  冬冬咚  阅读(15)  评论(0)    收藏  举报