二十六、“仿小红书”全栈项目微服务架构改造(一)

0 阅读7分钟

1.1 基于DDD的微服务代码结构设计与Maven项目组织

在基于领域驱动设计(DDD)的微服务架构中,代码结构的组织需要同时考虑领域模型的内聚性和服务的自治性。以下是针对您的"小红书"项目的详细设计方案:

DDD分层架构与服务封装原则

1. 领域模型优先原则

领域模型是核心,服务围绕领域模型构建:
- 每个微服务内部遵循DDD分层架构
- 领域模型在Domain层定义
- 服务在Application层协调领域逻辑

2. 微服务封装维度

建议按领域边界封装微服务:
- 文件领域微服务 (File Microservice)
- 用户领域微服务 (User Microservice)
- 内容领域微服务 (Content Microservice)
- 管理领域微服务 (Admin Microservice)

Maven项目结构设计

1. 多模块项目结构

rednote-microservices/
├── pom.xml (父项目)
├── rednote-common/               (公共模块)
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   ├── java/
│       │   │   └── com/waylau/rednote/common/
│       │   │       ├── exception/      (通用异常)
│       │   │       ├── util/           (通用工具类)
│       │   │       ├── dto/            (通用DTO)
│       │   │       └── config/         (通用配置)
│       │   └── resources/
│       │       └── application.properties     (公共配置)
│       └── test/
│           └── java/
│               └── com/waylau/rednote/common/
│                   └── util/           (工具类测试)
│
├── rednote-file-microservice/         (文件领域微服务)
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   ├── java/
│       │   │   └── com/waylau/rednote/filesmicroservice/
│       │   │       ├── Application.java (启动类)
│       │   │       ├── domain/         (领域层)
│       │   │       │   ├── model/      (领域模型)
│       │   │       │   │   ├── aggregate/ (聚合根)
│       │   │       │   │   ├── entity/   (实体)
│       │   │       │   │   └── valueobject/ (值对象)
│       │   │       │   ├── repository/ (领域仓储接口)
│       │   │       │   └── service/    (领域服务)
│       │   │       ├── application/    (应用层)
│       │   │       │   ├── service/    (应用服务)
│       │   │       │   └── dto/        (应用层DTO)
│       │   │       ├── infrastructure/ (基础设施层)
│       │   │       │   ├── repository/ (仓储实现)
│       │   │       │   ├── persistence/(持久化模型)
│       │   │       │   └── config/     (配置类)
│       │   │       └── interfaces/      (接口层)
│       │   │           ├── controller/ (控制器)
│       │   │           └── client/     (Feign客户端)
│       │   └── resources/
│       │       └── application.properties     (服务配置)
│       └── test/
│           └── java/
│               └── com/waylau/rednote/filesmicroservice/
│                   ├── domain/         (领域层测试)
│                   └── application/    (应用层测试)
│
├── rednote-user-microservice/         (用户领域微服务)
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   ├── java/
│       │   │   └── com/waylau/rednote/usermicroservice/
│       │   │       ├── Application.java
│       │   │       ├── domain/
│       │   │       │   ├── model/
│       │   │       │   ├── repository/
│       │   │       │   └── service/
│       │   │       ├── application/
│       │   │       │   ├── service/
│       │   │       │   └── dto/
│       │   │       ├── infrastructure/
│       │   │       │   ├── repository/
│       │   │       │   ├── persistence/
│       │   │       │   └── config/
│       │   │       └── interfaces/
│       │   │           ├── controller/
│       │   │           └── client/
│       │   └── resources/
│       │       └── application.properties
│       └── test/
│           └── java/
│               └── com/waylau/rednote/usermicroservice/
│
├── rednote-content-microservice/      (内容领域微服务)
│   ├── pom.xml
│   └── src/
│       ├── main/
│       │   ├── java/
│       │   │   └── com/waylau/rednote/contentmicroservice/
│       │   │       ├── Application.java
│       │   │       ├── domain/
│       │   │       │   ├── model/
│       │   │       │   ├── repository/
│       │   │       │   └── service/
│       │   │       ├── application/
│       │   │       │   ├── service/
│       │   │       │   └── dto/
│       │   │       ├── infrastructure/
│       │   │       │   ├── repository/
│       │   │       │   ├── persistence/
│       │   │       │   └── config/
│       │   │       └── interfaces/
│       │   │           ├── controller/
│       │   │           └── client/
│       │   └── resources/
│       │       └── application.properties
│       └── test/
│           └── java/
│               └── com/waylau/rednote/contentmicroservice/
│
└── rednote-admin-microservice/        (管理领域微服务)
    ├── pom.xml
    └── src/
        ├── main/
        │   ├── java/
        │   │   └── com/waylau/rednote/adminmicroservice/
        │   │       ├── Application.java
        │   │       ├── domain/
        │   │       │   ├── model/
        │   │       │   ├── repository/
        │   │       │   └── service/
        │   │       ├── application/
        │   │       │   ├── service/
        │   │       │   └── dto/
        │   │       ├── infrastructure/
        │   │       │   ├── repository/
        │   │       │   ├── persistence/
        │   │       │   └── config/
        │   │       └── interfaces/
        │   │           ├── controller/
        │   │           └── client/
        │   └── resources/
        │       └── application.properties
        └── test/
            └── java/
                └── com/waylau/rednote/adminmicroservice/

2. 依赖原则

1. 上层可以依赖下层,下层不能依赖上层
2. 领域层不依赖基础设施层具体实现
3. 服务间通过接口层暴露的API通信
4. 避免循环依赖

3. 领域模型完整性

- 每个微服务拥有自己完整的领域模型
- 领域模型不跨越服务边界
- 服务间通过DTO或事件进行协作

4. 服务自治性

- 每个微服务拥有独立的数据库
- 避免服务间直接访问数据库
- 通过API或消息队列进行通信

5. 渐进式迁移策略

1. 先拆分独立的领域服务 (如文件领域微服务)
2. 再拆分有依赖关系的服务 (如用户领域微服务)
3. 最后处理复杂的核心领域 (如内容领域微服务、管理领域微服务)

通过这种代码结构设计,您可以实现基于DDD的微服务架构,保持领域模型的完整性和服务的自治性,同时便于团队协作和系统维护。

1.2 实战基于DDD的微服务代码结构改造

按照DDD分层架构新建包名

  1. 修改项目名“rednote”为“rednote-microservices”
  2. 将原有项目的下创建按照DDD分层架构要求的包名。以文件领域微服务为例,包名结构如下图2-1所示。

图2-1 创建按照DDD分层架构要求的包名

按照DDD分层架构对代码进行重构

通过重构的方式,将原有项目的下移动到DDD分层架构的对应包名下。IntelliJ IDEA的重构功能如下图2-2所示。

图2-2 IntelliJ IDEA的重构功能

选中目标路径,点击“Refactor”按钮将代码进行移动。

图2-3 点击“Refactor”按钮将代码进行移动

清理冗余代码和包

  1. 原先废弃的代码和注释进行清理
  2. 被移走代码后,空缺的包进行删除
  3. 移除src/main/resources包下的static、templates目录。
  4. 移除spring-boot-starter-thymeleafthymeleaf-extras-springsecurity6spring-session-data-redis
  5. 删除以下配置信息
  6. 删除IndexController类、ErrorController类
# Thymeleaf 配置
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=UTF-8

# 会话超时
server.servlet.session.timeout=10m

# 配置 Spring Session
# 控制会话数据同步到 Redis 的时机,分别是 ON_SAVE 和 IMMEDIATE.
spring.session.redis.flush-mode=on_save
# 会话存储的key的命名空间,可以区分多应用下的key
spring.session.redis.namespace=spring:session:rednote

最终,代码结构如下图2-4所示。

图2-4 代码结构

运行调测

重构理论上不应该修改现有的业务逻辑。因此,重构完成之后,应运行调测确保启动正常、功能完整。

1.3 基于DDD的Maven多模块化改造

Maven多模块项目允许你将一个大型项目拆分为多个子模块,每个模块可以独立开发、构建和部署。下面是一个完整的多模块项目配置示例。

项目结构

rednote-microservices/
├── pom.xml (父POM)
├── rednote-common/               (公共模块)
│   ├── pom.xml
│   └── src/
│── rednote-file-microservice/         (文件领域微服务模块)
│   ├── pom.xml
│   └── src/
├── rednote-user-microservice/         (用户领域微服务模块)
│   ├── pom.xml
│   └── src/
├── rednote-content-microservice/      (内容领域微服务模块)
│   ├── pom.xml
│   └── src/
└── rednote-admin-microservice/        (管理领域微服务模块)
    ├── pom.xml
    └── src/

关键点说明

  1. 父POM

    • packaging 必须为 pom
    • 通过 <modules> 声明所有子模块
    • 使用 <dependencyManagement> 统一管理依赖版本
    • 使用 <pluginManagement> 统一管理插件配置
  2. 子模块

    • 必须声明父POM的坐标
    • 可以继承父POM的依赖管理和插件管理
    • 可以定义自己的依赖和插件
    • 可以通过 ${project.version} 引用父POM的版本号
  3. 构建命令

    • 构建整个项目:mvn clean install
    • 构建特定模块:mvn clean install -pl rednote-user-microservice
    • 构建多个模块:mvn clean install -pl rednote-common,rednote-file-microservice
    • 构建并跳过测试:mvn clean install -DskipTests

父模块rednote-microservices

<?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.5.0</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.waylau</groupId>
	<artifactId>rednote-microservices</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>pom</packaging>  <!-- 父POM的packaging必须是pom -->
	<name>rednote-microservices</name>
	<description>RedNote. 仿“小红书”项目</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>

	<!-- 模块列表 -->
	<modules>
		<module>rednote-common</module>
		<module>rednote-file-microservice</module>
		<module>rednote-user-microservice</module>
		<module>rednote-content-microservice</module>
		<module>rednote-admin-microservice</module>
	</modules>

	<properties>
		<java.version>24</java.version>
		<mysql-connector-j.version>9.3.0</mysql-connector-j.version>
		<jsonwebtoken.version>0.12.6</jsonwebtoken.version>
    <jakarta.persistence-api.version>3.1.0</jakarta.persistence-api.version>
	</properties>

	<dependencies>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.kafka</groupId>
			<artifactId>spring-kafka</artifactId>
		</dependency>

		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt-api</artifactId>
			<version>${jsonwebtoken.version}</version>
		</dependency>
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt-impl</artifactId>
			<version>${jsonwebtoken.version}</version>
		</dependency>
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt-jackson</artifactId>
			<version>${jsonwebtoken.version}</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<annotationProcessorPaths>
						<path>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</path>
					</annotationProcessorPaths>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

通过IntelliJ IDEA,在父POM中点击模块列表中的模块,可以自动创建子模块,如下图2-5所示。

图2-5 自动创建子模块

最终,项目结构如下图2-6所示。

图2-6 项目结构

子模块rednote-common/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>
        <groupId>com.waylau</groupId>
        <artifactId>rednote-microservices</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.waylau</groupId>
    <artifactId>rednote-common</artifactId>
    <name>rednote-common</name>
    <packaging>jar</packaging>
    <description>公共模块</description>

    <!-- 子模块可以定义自己的依赖 -->
    <dependencies>
        <!--因移除了spring-boot-starter-data-jpa,因此需要手动添加-->
        <dependency>
            <groupId>jakarta.persistence</groupId>
            <artifactId>jakarta.persistence-api</artifactId>
            <version>${jakarta.persistence-api.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>
</project>

子模块rednote-file-microservice/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>
        <groupId>com.waylau</groupId>
        <artifactId>rednote-microservices</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.waylau</groupId>
    <artifactId>rednote-file-microservice</artifactId>
    <name>rednote-file-microservice</name>
    <packaging>jar</packaging>
    <description>文件领域微服务模块</description>

    <!-- 子模块可以定义自己的依赖 -->
    <dependencies>
        <!-- 依赖公共模块 -->
        <dependency>
            <groupId>com.waylau</groupId>
            <artifactId>rednote-common</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
    </dependencies>

    <!-- 子模块可以定义自己的插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

子模块rednote-user-microservice/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>
        <groupId>com.waylau</groupId>
        <artifactId>rednote-microservices</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.waylau</groupId>
    <artifactId>rednote-user-microservice</artifactId>
    <name>rednote-user-microservice</name>
    <packaging>jar</packaging>
    <description>用户领域微服务模块</description>

    <!-- 子模块可以定义自己的依赖 -->
    <dependencies>
        <!-- 依赖公共模块 -->
        <dependency>
            <groupId>com.waylau</groupId>
            <artifactId>rednote-common</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
            <version>${mysql-connector-j.version}</version>
        </dependency>
    </dependencies>

    <!-- 子模块可以定义自己的插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

子模块rednote-admin-microservice/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>
        <groupId>com.waylau</groupId>
        <artifactId>rednote-microservices</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.waylau</groupId>
    <artifactId>rednote-admin-microservice</artifactId>
    <name>rednote-admin-microservice</name>
    <packaging>jar</packaging>
    <description>管理领域微服务模块</description>

    <!-- 子模块可以定义自己的依赖 -->
    <dependencies>
        <!-- 依赖公共模块 -->
        <dependency>
            <groupId>com.waylau</groupId>
            <artifactId>rednote-common</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

    <!-- 子模块可以定义自己的插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

子模块rednote-content-microservice/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>
        <groupId>com.waylau</groupId>
        <artifactId>rednote-microservices</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.waylau</groupId>
    <artifactId>rednote-content-microservice</artifactId>
    <name>rednote-content-microservice</name>
    <packaging>jar</packaging>
    <description>内容领域微服务模块</description>

    <!-- 子模块可以定义自己的依赖 -->
    <dependencies>
        <!-- 依赖公共模块 -->
        <dependency>
            <groupId>com.waylau</groupId>
            <artifactId>rednote-common</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
            <version>${mysql-connector-j.version}</version>
        </dependency>
    </dependencies>

    <!-- 子模块可以定义自己的插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

最佳实践

  1. 将公共依赖和插件配置放在父POM中
  2. 使用属性管理版本号,便于统一升级
  3. 合理划分模块,避免循环依赖
  4. 为每个模块定义清晰的职责
  5. 考虑使用Maven的聚合和继承特性分离关注点

这样的多模块结构适合中大型项目,可以提高代码复用性、降低耦合度,并简化项目维护。

1.4 现有代码迁移至指定模块下

按照包名迁移至指定模块下

按照包名将代码整体迁移至与包名对应的指定模块。

领域微服务模块放置启动类

四个领域微服务下放置启动类RednoteApplication。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RednoteApplication {

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

}

领域微服务模块放置应用配置

rednote-file-microservice

spring.application.name=rednote-file-microservice
server.port=9010


# 文件上传配置
file.upload-dir=/data/rednote
file.static-path-prefix=/uploads/

# 上传文件大小限制
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

# 配置内嵌的 tomcat 的最大吞吐量
server.tomcat.max-swallow-size = 100MB

# 配置 MongoDB
spring.data.mongodb.uri=mongodb://localhost:27017
spring.data.mongodb.grid-fs-database=rednote_files
spring.data.mongodb.database=rednote

# 配置 JWT
# 你的Base64编码密钥(至少256位)
app.jwtSecret=bQUBj9U7io0VXuhlaC9XmeaSGSwkqOlG4itHzIgUvOk=
# 24小时
app.jwtExpirationMs=86400000

rednote-user-microservice

spring.application.name=rednote-user-microservice
server.port=9020

# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/rednote
spring.datasource.username=root
spring.datasource.password=123456

# 启动时更新表结构,添加缺少的列,修改已有列类型等,但不会删除任何东西。
spring.jpa.properties.hibernate.hbm2ddl.auto=update
# 显示SQL
spring.jpa.show-sql=true

# 管理员配置
admin.username=admin
admin.password=admin123

# 配置 JWT
# 你的Base64编码密钥(至少256位)
app.jwtSecret=bQUBj9U7io0VXuhlaC9XmeaSGSwkqOlG4itHzIgUvOk=
# 24小时
app.jwtExpirationMs=86400000

rednote-content-microservice

spring.application.name=rednote-content-microservice
server.port=9030

# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/rednote
spring.datasource.username=root
spring.datasource.password=123456

# 启动时更新表结构,添加缺少的列,修改已有列类型等,但不会删除任何东西。
spring.jpa.properties.hibernate.hbm2ddl.auto=update
# 显示SQL
spring.jpa.show-sql=true

# 文件上传配置
file.upload-dir=/data/rednote
file.static-path-prefix=/uploads/

# 上传文件大小限制
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

# 配置内嵌的 tomcat 的最大吞吐量
server.tomcat.max-swallow-size = 100MB

# 配置 Spring Data Redis
spring.data.redis.host: localhost
spring.data.redis.port: 6379
spring.data.redis.password:

# 配置 Kafka
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.producer.retries=3
spring.kafka.producer.batch-size=16384
spring.kafka.producer.buffer-memory=33554432
spring.kafka.consumer.group-id=rednote-group
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.consumer.properties.spring.json.trusted.packages=com.waylau.rednote.*

# 配置 JWT
# 你的Base64编码密钥(至少256位)
app.jwtSecret=bQUBj9U7io0VXuhlaC9XmeaSGSwkqOlG4itHzIgUvOk=
# 24小时
app.jwtExpirationMs=86400000

rednote-admin-microservice

spring.application.name=rednote-admin-microservice
server.port=9040

# 配置 Spring Data Redis
spring.data.redis.host: localhost
spring.data.redis.port: 6379
spring.data.redis.password:

# 配置 Kafka
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.producer.retries=3
spring.kafka.producer.batch-size=16384
spring.kafka.producer.buffer-memory=33554432
spring.kafka.consumer.group-id=rednote-group
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.consumer.properties.spring.json.trusted.packages=com.waylau.rednote.*

# 配置 JWT
# 你的Base64编码密钥(至少256位)
app.jwtSecret=bQUBj9U7io0VXuhlaC9XmeaSGSwkqOlG4itHzIgUvOk=
# 24小时
app.jwtExpirationMs=86400000

最终,代码结构如下图2-7所示。

图2-7 代码结构

2.1 引入Spring Cloud微服务框架

添加Spring Cloud依赖管理

修改父模块pom.xml

<properties>
    <!--...为节约篇幅,此处省略非核心内容-->
    <spring-cloud.version>2025.0.0</spring-cloud.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>
    </dependencies>
</dependencyManagement>

添加Spring Cloud Alibaba依赖管理

修改父模块pom.xml

<properties>
    <!--...为节约篇幅,此处省略非核心内容-->
    <spring-cloud-alibaba.version>2025.0.0.0</spring-cloud-alibaba.version>
</properties>

<!--...为节约篇幅,此处省略非核心内容-->
<dependencyManagement>
    <dependencies>
			<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>

2.2 API网关微服务的设计与实现方案

在微服务架构中,API网关是前端与后端服务的桥梁,负责请求路由、认证授权、流量控制等核心功能。针对您的"小红书"项目,引入API网关微服务需要从架构设计、技术选型、配置实现等多方面考虑。

API网关的核心功能定位

1. 核心职责

- 请求路由:将前端请求转发到对应的微服务
- 认证授权:统一校验用户身份和权限
- 流量控制:实现限流、熔断、降级等保护机制
- 协议转换:适配不同微服务的协议和接口格式
- 请求增强:添加请求头、参数校验、日志记录等

2. 不建议在网关层实现的功能

- 复杂业务逻辑处理
- 数据持久化操作
- 与特定业务领域强相关的功能

创建新模块

修改父模块pom.xml:

<!-- 模块列表 -->
<modules>
	<!--...为节约篇幅,此处省略非核心内容-->
	<module>rednote-gateway-microservice</module>
</modules>

上述配置:

  • 创建了新的模块rednote-gateway-microservice代表API网关领域微服务模块

修改自己的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>
        <groupId>com.waylau</groupId>
        <artifactId>rednote-microservices</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.waylau</groupId>
    <artifactId>rednote-gateway-microservice</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>rednote-gateway-microservice</name>
    <packaging>jar</packaging>
    <description>API网关领域微服务模块</description>

    <!-- 子模块可以定义自己的依赖包 -->
    <dependencies>
    <!--...为节约篇幅,此处省略非核心内容-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway-server-webmvc</artifactId>
        </dependency>
    </dependencies>

    <!-- 子模块可以定义自己的插件 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

上述配置:

  • 引入了Spring Cloud Gateway

创建启动类

src/main/java/com/waylau/rednote/gatewaymicroservice目录下创建启动类:

package com.waylau.rednote.gatewaymicroservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RednoteApplication {

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

}

增加配置

在应用配置中添加如下配置:

spring.application.name=rednote-gateway-microservice
server.port=8080

# 配置 JWT
# 你的Base64编码密钥(至少256位)
app.jwtSecret=bQUBj9U7io0VXuhlaC9XmeaSGSwkqOlG4itHzIgUvOk=
# 24小时
app.jwtExpirationMs=86400000

路由配置

在应用配置中添加如下配置:

# 配置 Spring Cloud Gateway
spring.cloud.gateway.server.webmvc.enabled=true

# 路由配置
spring.cloud.gateway.mvc.routes[0].id=rednote-user-microservice
spring.cloud.gateway.mvc.routes[0].uri=lb://rednote-user-microservice
spring.cloud.gateway.mvc.routes[0].predicates[0]=Path=/user/**,/auth/**
spring.cloud.gateway.mvc.routes[1].id=rednote-content-microservice
spring.cloud.gateway.mvc.routes[1].uri=lb://rednote-content-microservice
spring.cloud.gateway.mvc.routes[1].predicates[0]=Path=/note/**,/explore/**,/comment/**,/like/**,/log/**
spring.cloud.gateway.mvc.routes[2].id=rednote-file-microservice
spring.cloud.gateway.mvc.routes[2].uri=lb://rednote-file-microservice
spring.cloud.gateway.mvc.routes[2].predicates[0]=Path=/file/**,/uploads/**
spring.cloud.gateway.mvc.routes[3].id=rednote-admin-microservice
spring.cloud.gateway.mvc.routes[3].uri=lb://rednote-admin-microservice
spring.cloud.gateway.mvc.routes[3].predicates[0]=Path=/admin/**

2.3 开启服务调用与负载均衡功能

spring-cloud-loadbalancer 是 Spring Cloud 提供的 客户端负载均衡器,用于在微服务架构中动态选择服务实例,实现请求的负载分发。它是 Netflix Ribbon 的替代方案(Ribbon 已停止维护),与 Spring Cloud Gateway、Feign、RestTemplate 等组件无缝集成,支持多种负载均衡策略。

添加依赖

修改父模块pom.xml

<dependencies>
  <!--...为节约篇幅,此处省略非核心内容-->
  <dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-loadbalancer</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-openfeign</artifactId>
	</dependency>
</dependencies>

增加配置

在所有领域微服务应用配置中添加如下配置:

# 配置 Spring Cloud Loadbalancer
spring.cloud.loadbalancer.enabled=true

2.4 开启服务注册与发现功能

添加依赖

修改父模块pom.xml

<properties>
  <!--...为节约篇幅,此处省略非核心内容-->
	<nacos.version>3.1.1</nacos.version>
</properties>

<dependencies>
  <!--...为节约篇幅,此处省略非核心内容-->
  <dependency>
		<groupId>com.alibaba.cloud</groupId>
		<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  </dependency>
</dependencies>

<dependencyManagement>
		<dependencies>
				<!--...为节约篇幅,此处省略非核心内容-->
				
				<!-- 强制指定 nacos-client 版本-->
				<dependency>
						<groupId>com.alibaba.nacos</groupId>
						<artifactId>nacos-client</artifactId>
						<version>${nacos.version}</version>
				</dependency>
		</dependencies>
</dependencyManagement>

启用功能

在所有领域微服务应用的RednoteApplication中添加@EnableDiscoveryClient

package com.waylau.rednote.filesmicroservice;

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

@SpringBootApplication
@EnableDiscoveryClient
public class RednoteApplication {

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

}

增加配置

在所有领域微服务应用配置中添加如下配置:

# 配置 Nacos
spring.cloud.loadbalancer.nacos.enabled=true
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
management.endpoints.web.exposure.include=*

2.5 开启分布式配置管理

添加依赖

修改父模块pom.xml

<dependencies>
  <!--...为节约篇幅,此处省略非核心内容-->
  <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  </dependency>
</dependencies>

增加配置

在所有领域微服务应用配置中添加如下配置:

spring.cloud.nacos.config.server-addr=127.0.0.1:8848

并针对各个应用的应用名称不同,分别增加以下配置

spring.config.import[0]=nacos:rednote-file-microservice.properties?group=DEFAULT_GROUP
spring.config.import[0]=nacos:rednote-user-microservice.properties?group=DEFAULT_GROUP
spring.config.import[0]=nacos:rednote-content-microservice.properties?group=DEFAULT_GROUP
spring.config.import[0]=nacos:rednote-admin-microservice.properties?group=DEFAULT_GROUP
spring.config.import[0]=nacos:rednote-gateway-microservice.properties?group=DEFAULT_GROUP

2.6 开启分布式事务功能

添加依赖

修改父模块pom.xml

<dependencies>
  <!--...为节约篇幅,此处省略非核心内容-->
  <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
  </dependency>
</dependencies>

增加配置

在所有领域微服务应用配置中添加如下配置:

# 配置 Seata
seata.tx-service-group=rednote_tx_group
seata.service.vgroup-mapping.rednote_tx_group=default
seata.registry.type=nacos
seata.registry.nacos.server-addr=127.0.0.1:8848
seata.registry.nacos.namespace=public
seata.registry.nacos.cluster=default

3.1 新增文件上传接口

修改FileController,新增文件上传接口:

/**
 * 上传文件
 *
 * @param file
 * @return
 */
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<String> uploadImage(@RequestPart("file") MultipartFile file) {
    String fileId = null;

    // 验证文件
    if (file != null && !file.isEmpty()) {
        // 处理文件上传
        fileId = gridFSStorageService.uploadImage(file);
    }

    return ResponseEntity.ok(fileId);
}

服务间调用使用 Feign 客户端,文件上传需要通过 multipart/form-data 格式传输,但 Feign 客户端默认不会自动设置该格式,也不会正确处理 MultipartFile 类型参数,导致服务端接收的请求不是 multipart 类型。Feign 客户端必须显式指定请求类型为 multipart/form-data,并使用 @RequestPart 注解接收文件。

3.2 新增文件删除接口

修改FileController,新增文件删除接口:

/**
 * 删除文件
 *
 * @param fileId
 * @return
 */
@DeleteMapping("/{fileId}")
public ResponseEntity<String> deleteImage(@PathVariable String fileId) {
    // 验证文件是否存在
    GridFSFile file = gridFSStorageService.downloadImage(fileId);

    if (file == null) {
        return ResponseEntity.ok("文件不存在");
    }

    gridFSStorageService.deleteImage(fileId);

    return ResponseEntity.ok("文件删除成功");
}