SpringCloud 微服务架构与 Nacos

注:完整笔记可在 https://github.com/WuSangui571/cloud-demo 中的 README.md 文件浏览,该笔记是记录从 2025 年 9 月 21 日开始的 SpringCloud 学习过程,此处发表的是经由 AI 润色过的精简版。

1. 分布式架构概述

在项目部署中,常见的架构类型包括单体架构、集群架构和分布式架构。以下分别介绍其特点与适用场景。

1.1 单体架构

单体架构将整个应用程序部署在单一服务器上,用户通过域名访问绑定的公网 IP 地址。

优点

  • 部署简单,适合小型项目或初期开发。

缺点

  • 高并发场景下,单点故障风险高,容易导致服务不可用。

1.2 集群架构

集群架构通过网关(如 Nginx)将用户请求分发到多个服务器,每个服务器部署相同的应用程序。

优点

  • 有效应对高并发,降低单点故障风险。

缺点

  • 无法模块化升级维护,难以支持多语言开发。

1.3 分布式架构

分布式架构将应用程序拆分为多个功能模块,部署在不同服务器上,通过网关分发用户请求。

优点

  • 支持高并发,模块间独立,可使用多种语言开发,便于维护和升级。

  • 单一模块故障不影响全局服务。

缺点

  • 系统复杂性增加,需依赖服务注册与配置管理工具(如 Nacos)。

2. 搭建首个 SpringCloud 微服务项目

本节以一个简单的订单与商品微服务项目为例,展示 SpringCloud 的基本搭建流程。

2.1 项目版本选型

为确保兼容性,明确项目使用的框架和组件版本如下:

框架/组件版本
SpringBoot3.3.4
SpringCloud2023.0.3
SpringCloud Alibaba2023.0.3.2
Nacos2.4.3
Sentinel1.8.8
Seata2.2.0

2.2 创建 SpringBoot 项目

  1. 创建一个新的 SpringBoot 项目,命名为 cloud-demo

  2. 删除除 .ideapom.xml 外的所有文件。

  3. 修改 pom.xml,添加 SpringCloud 和 SpringCloud Alibaba 依赖,并配置打包方式为 pom

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>3.3.4</version>
       <relativePath/>
   </parent>
   <packaging>pom</packaging>
   <groupId>com.sangui</groupId>
   <artifactId>cloud-demo</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>cloud-demo</name>
   <description>SpringCloud 微服务演示项目</description>
   <properties>
       <maven.compiler.source>21</maven.compiler.source>
       <maven.compiler.target>21</maven.compiler.target>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <spring-cloud.version>2023.0.3</spring-cloud.version>
       <spring-cloud-alibaba.version>2023.0.3.2</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>
   <build>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
           </plugin>
       </plugins>
   </build>
</project>

2.3 创建服务模块

  1. cloud-demo 下创建子模块 services,设置为 pom 打包方式,删除其 src 文件夹。

  2. 修改 servicespom.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">
   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>com.sangui</groupId>
       <artifactId>cloud-demo</artifactId>
       <version>0.0.1-SNAPSHOT</version>
   </parent>
   <packaging>pom</packaging>
   <artifactId>services</artifactId>
   <properties>
       <maven.compiler.source>21</maven.compiler.source>
       <maven.compiler.target>21</maven.compiler.target>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   </properties>
</project>
  1. services 下创建两个微服务模块:service-productservice-order,均为普通 Java 模块。

2.4 配置公共模型模块

为避免重复定义实体类,在 cloud-demo 下创建 model 模块,存放公共实体类(如 ProductOrder)。在 service-productservice-orderpom.xml 中添加 model 依赖:

<dependency>
   <groupId>com.sangui</groupId>
   <artifactId>model</artifactId>
   <version>0.0.1-SNAPSHOT</version>
</dependency>

3. Nacos 服务注册与发现

Nacos(Dynamic Naming and Configuration Service)是一个云原生应用的动态服务发现、配置管理和服务管理平台,官方地址:https://nacos.io

3.1 Nacos 安装与启动

  1. 下载:从 https://nacos.io/download/nacos-server/ 下载 Nacos 2.4.3 二进制包。

  2. 启动:解压后进入 bin 目录,执行以下命令以单机模式启动(Windows 环境):

startup.cmd -m standalone
  1. 访问:打开浏览器,输入 http://localhost:8848/nacos/,即可访问 Nacos 管理界面,核心功能包括配置管理服务管理

3.2 服务注册

  1. 添加依赖

    • cloud-demopom.xml 中添加 Nacos 服务发现依赖:

<dependency>
   <groupId>com.alibaba.cloud</groupId>
   <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  • 在子模块(如 service-order)中添加 Web 依赖:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  1. 配置微服务

    • service-orderapplication.yaml 中配置服务名称和 Nacos 地址:

spring:
application:
  name: service-order
cloud:
  nacos:
    server-addr: 127.0.0.1:8848
server:
port: 8000
  1. 编写主入口程序

    • 示例:service-order 的主程序:

package com.sangui.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
* @author sangui
* @createTime 2025-09-21
* @description 订单微服务主入口
* @version 1.0
*/
@EnableDiscoveryClient
@SpringBootApplication
public class OrderMainApplication {
   public static void main(String[] args) {
       SpringApplication.run(OrderMainApplication.class, args);
  }
}
  1. 验证注册

    • 启动 service-orderservice-product(端口分别为 8000 和 9000)。

    • 访问 Nacos 管理界面,查看服务列表,确认微服务注册成功。

  2. 模拟集群

    • 通过修改端口号(如 8001、9001、9002)启动多个实例,Nacos 服务列表将显示实例数量(如 service-order 2 个实例,service-product 3 个实例)。

3.3 服务发现

  1. 启用服务发现

    • 在微服务主类上添加 @EnableDiscoveryClient 注解。

  2. 测试服务发现

    • 使用 DiscoveryClientNacosServiceDiscovery 查询服务实例:

package com.sangui.product;

import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;

import java.util.List;

/**
* @author sangui
* @createTime 2025-09-22
* @description 服务发现测试
* @version 1.0
*/
@SpringBootTest
public class DiscoveryTest {
   @Resource
   private DiscoveryClient discoveryClient;

   @Test
   void discoveryClientTest() {
       for (String service : discoveryClient.getServices()) {
           System.out.println("服务名称: " + service);
           List<ServiceInstance> instances = discoveryClient.getInstances(service);
           for (ServiceInstance instance : instances) {
               System.out.println("服务: " + service + ",IP: " + instance.getHost() + ",端口: " + instance.getPort());
          }
      }
  }
}

3.4 微服务 API 开发

以订单和商品服务交互为例,展示微服务间远程调用。

  1. 商品服务

    • 定义 Product 实体类(位于 model 模块):

package com.sangui.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;

/**
* @author sangui
* @createTime 2025-09-22
* @description 商品实体类
* @version 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
   private Long id;
   private BigDecimal price;
   private String productName;
   private Integer number;
}
  • 实现商品服务接口:

package com.sangui.product.controller;

import com.sangui.model.Product;
import com.sangui.product.service.ProductService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
* @author sangui
* @createTime 2025-09-22
* @description 商品服务控制器
* @version 1.0
*/
@RestController
public class ProductController {
   @Resource
   private ProductService productService;

   @GetMapping("/product/{id}")
   public Product getProduct(@PathVariable("id") Long productId) {
       return productService.getProductById(productId);
  }
}
  1. 订单服务

    • 定义 Order 实体类(位于 model 模块):

package com.sangui.model;

import lombok.Data;

import java.math.BigDecimal;
import java.util.List;

/**
* @author sangui
* @createTime 2025-09-22
* @description 订单实体类
* @version 1.0
*/
@Data
public class Order {
   private Long id;
   private BigDecimal totalAmount;
   private Long userId;
   private String nickName;
   private String address;
   private List<Product> productList;
}
  • 实现订单服务接口,包含远程调用商品服务:

package com.sangui.order.service.impl;

import com.sangui.model.Order;
import com.sangui.model.Product;
import com.sangui.order.service.OrderService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.Arrays;

/**
* @author sangui
* @createTime 2025-09-22
* @description 订单服务实现
* @version 1.0
*/
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
   @Resource
   private DiscoveryClient discoveryClient;

   @Resource
   private RestTemplate restTemplate;

   @Override
   public Order createOrder(Long productId, Long userId) {
       Product product = getProductFromRemote(productId);
       Order order = new Order();
       order.setId(1011L);
       order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNumber())));
       order.setUserId(userId);
       order.setNickName("张三");
       order.setAddress("北京");
       order.setProductList(Arrays.asList(product));
       return order;
  }

   private Product getProductFromRemote(Long productId) {
       List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
       ServiceInstance instance = instances.get(0);
       String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/product/" + productId;
       log.info("远程请求 URL: {}", url);
       return restTemplate.getForObject(url, Product.class);
  }
}
  1. 测试

    • 访问 http://localhost:8000/create?productId=1&userId=2,返回订单信息,包含商品数据。

3.5 负载均衡

为实现服务实例的负载均衡,引入 SpringCloud LoadBalancer。

  1. 添加依赖

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
  1. 配置 RestTemplate

    • OrderServiceConfig 中添加 @LoadBalanced 注解:

package com.sangui.order.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
* @author sangui
* @createTime 2025-09-22
* @description 订单服务配置
* @version 1.0
*/
@Configuration
public class OrderServiceConfig {
   @Bean
   @LoadBalanced
   public RestTemplate restTemplate() {
       return new RestTemplate();
  }
}
  1. 优化远程调用

    • 使用服务名直接调用,无需手动指定实例:

private Product getProductFromRemoteWithLoadBalance(Long productId) {
   String url = "http://service-product/product/" + productId;
   log.info("远程请求 URL: {}", url);
   return restTemplate.getForObject(url, Product.class);
}
  1. 验证负载均衡

    • 多次调用订单创建接口,日志显示请求均匀分发到 service-product 的不同实例(端口 9000、9001、9002)。

3.6 Nacos 配置中心

Nacos 提供动态配置管理功能,支持配置的集中管理和实时刷新。

  1. 添加依赖

<dependency>
   <groupId>com.alibaba.cloud</groupId>
   <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
  1. 配置导入

    • application.yaml 中指定导入的 Nacos 配置文件:

spring:
config:
  import:
    - nacos:service-order.yaml
  1. 创建 Nacos 配置

    • 在 Nacos 管理界面创建配置文件(如 service-order.yaml),示例内容:

order:
timeout: 30min
auto-confirm: 7d
  1. 动态刷新

    • 使用 @ConfigurationProperties 实现配置自动刷新:

package com.sangui.order.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
* @author sangui
* @createTime 2025-09-22
* @description 订单配置类
* @version 1.0
*/
@ConfigurationProperties(prefix = "order")
@Component
@Data
public class OrderProperties {
   private String timeout;
   private String autoConfirm;
}
  • 在控制器中使用:

package com.sangui.order.controller;

import com.sangui.model.Order;
import com.sangui.order.properties.OrderProperties;
import com.sangui.order.service.OrderService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
* @author sangui
* @createTime 2025-09-22
* @description 订单控制器
* @version 1.0
*/
@RestController
public class OrderController {
   @Resource
   private OrderProperties orderProperties;

   @Resource
   private OrderService orderService;

   @GetMapping("/create")
   public Order createOrder(@RequestParam("productId") Long productId,
                            @RequestParam("userId") Long userId) {
       return orderService.createOrder(productId, userId);
  }

   @GetMapping("/config")
   public String config() {
       return "orderTimeout=" + orderProperties.getTimeout() + ",orderAutoConfirm=" + orderProperties.getAutoConfirm();
  }
}
  1. 配置监听

    • 通过 NacosConfigManager 监听配置文件变化:

package com.sangui.order;

import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;

import java.util.concurrent.Executors;

/**
* @author sangui
* @createTime 2025-09-21
* @description 订单微服务主入口
* @version 1.0
*/
@EnableDiscoveryClient
@SpringBootApplication
public class OrderMainApplication {
   @Bean
   ApplicationRunner applicationRunner(NacosConfigManager nacosConfigManager) {
       return args -> {
           ConfigService configService = nacosConfigManager.getConfigService();
           configService.addListener("service-order.yaml", "DEFAULT_GROUP", new Listener() {
               @Override
               public java.util.concurrent.Executor getExecutor() {
                   return Executors.newFixedThreadPool(4);
              }

               @Override
               public void receiveConfigInfo(String configInfo) {
                   System.out.println("配置文件变更: " + configInfo);
                   System.out.println("邮件通知(模拟)...");
              }
          });
      };
  }

   public static void main(String[] args) {
       SpringApplication.run(OrderMainApplication.class, args);
  }
}

3.7 配置冲突与优先级

当 Nacos 配置与本地 application.yaml 冲突时,Nacos 配置优先生效。优先级规则:

  • 外部配置(如 Nacos)优先于本地配置。

  • 若导入多个配置文件,先导入的配置优先。

3.8 多环境配置隔离

为支持多环境(如 testdevprod),Nacos 使用命名空间(namespace)、分组(group)和数据 ID(Data ID)实现配置隔离。

  1. 配置示例

spring:
profiles:
  active: test
cloud:
  nacos:
    config:
      namespace: ${spring.profiles.active:dev}
      import-check:
        enabled: false

---
spring:
config:
  import:
    - nacos:service-order1.yaml?group=order
    - nacos:service-order2.yaml?group=order
  activate:
    on-profile: test

---
spring:
config:
  import:
    - nacos:service-order1.yaml?group=order
    - nacos:service-order2.yaml?group=order
    - nacos:service-order3.yaml?group=order
  activate:
    on-profile: dev
  1. 说明

    • 命名空间:区分不同环境(如 testdev)。

    • 分组:区分不同微服务(如 order)。

    • Data ID:区分同一微服务的不同配置文件。

    • 激活配置:通过 spring.profiles.active 切换环境。

3.9 Nacos 注册中心高可用性

问题:若 Nacos 注册中心宕机,微服务远程调用是否受影响?

解答

  • 已调用过注册中心:微服务会缓存实例信息,宕机后仍可通过缓存调用,影响较小,但无法实时更新实例状态。

  • 未调用过注册中心:首次调用无法获取实例信息,远程调用失败。

  • 优化建议:部署 Nacos 集群以提高高可用性,避免单点故障。


  • 微信
  • 赶快加我聊天吧
  • QQ
  • 赶快加我聊天吧
  • weinxin
三桂

发表评论 取消回复 您未登录,登录后才能评论,前往登录