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分层架构新建包名
- 修改项目名“rednote”为“rednote-microservices”
- 将原有项目的下创建按照DDD分层架构要求的包名。以文件领域微服务为例,包名结构如下图2-1所示。
按照DDD分层架构对代码进行重构
通过重构的方式,将原有项目的下移动到DDD分层架构的对应包名下。IntelliJ IDEA的重构功能如下图2-2所示。
选中目标路径,点击“Refactor”按钮将代码进行移动。
清理冗余代码和包
- 原先废弃的代码和注释进行清理
- 被移走代码后,空缺的包进行删除
- 移除
src/main/resources包下的static、templates目录。 - 移除
spring-boot-starter-thymeleaf、thymeleaf-extras-springsecurity6、spring-session-data-redis - 删除以下配置信息
- 删除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所示。
运行调测
重构理论上不应该修改现有的业务逻辑。因此,重构完成之后,应运行调测确保启动正常、功能完整。
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/
关键点说明
-
父POM:
packaging必须为pom- 通过
<modules>声明所有子模块 - 使用
<dependencyManagement>统一管理依赖版本 - 使用
<pluginManagement>统一管理插件配置
-
子模块:
- 必须声明父POM的坐标
- 可以继承父POM的依赖管理和插件管理
- 可以定义自己的依赖和插件
- 可以通过
${project.version}引用父POM的版本号
-
构建命令:
- 构建整个项目:
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-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>
最佳实践
- 将公共依赖和插件配置放在父POM中
- 使用属性管理版本号,便于统一升级
- 合理划分模块,避免循环依赖
- 为每个模块定义清晰的职责
- 考虑使用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.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("文件删除成功");
}