Spring Cloud Alibaba 实战:从 0 到 1 构建可监控的微服务体系

发布于:2025-09-16 ⋅ 阅读:(20) ⋅ 点赞:(0)

引言:微服务架构的现状与挑战

在当今互联网技术快速迭代的背景下,单体应用架构已难以满足业务快速发展的需求。微服务架构通过将应用拆分为一系列小型、自治的服务,实现了技术栈多样化、团队独立开发和部署等优势,但同时也带来了服务治理、分布式协调、链路追踪等新的挑战。

Spring Cloud Alibaba 作为国内最流行的微服务解决方案之一,整合了阿里巴巴中间件生态与 Spring Cloud 标准,为开发者提供了一套完整的微服务治理方案。本文将带您从搭建基础框架开始,逐步实现服务注册发现、配置中心、熔断限流、网关路由等核心功能,并最终构建一套完善的监控告警体系,全方位掌握微服务从构建到运维的全链路实践。

一、环境准备与技术选型

1.1 开发环境

  • JDK: 17.0.10
  • Maven: 3.9.6
  • MySQL: 8.0.36
  • Docker: 26.1.4 (用于部署中间件)
  • IDE: IntelliJ IDEA 2024.1

1.2 技术栈选型

组件 功能 版本
Spring Boot 应用开发基础框架 3.2.6
Spring Cloud 微服务标准规范 2023.0.2
Spring Cloud Alibaba 微服务组件套件 2023.0.1.0
Nacos 服务注册与配置中心 2.3.2
Sentinel 熔断与限流 1.8.7
Spring Cloud Gateway 网关服务 4.1.2
OpenFeign 服务调用 4.1.2
MyBatis-Plus ORM 框架 3.5.6
Seata 分布式事务 2.0.0
SkyWalking 分布式链路追踪 9.7.0
Prometheus metrics 收集 2.45.0
Grafana 监控可视化 10.4.2
Lombok 简化 Java 代码 1.18.30
Fastjson2 JSON 处理 2.0.47
Knife4j Swagger3 增强 4.4.0

1.3 整体架构设计

我们将构建一个包含以下服务的微服务系统:

二、基础环境搭建

2.1 安装与配置 Nacos

Nacos 是 Spring Cloud Alibaba 生态中的核心组件,提供服务注册发现和配置管理功能。我们使用 Docker 快速部署:

# 拉取镜像
docker pull nacos/nacos-server:v2.3.2

# 启动容器
docker run -d \
  --name nacos \
  -p 8848:8848 \
  -p 9848:9848 \
  -p 9849:9849 \
  -e MODE=standalone \
  -e JVM_XMS=512m \
  -e JVM_XMX=512m \
  nacos/nacos-server:v2.3.2

访问 http://localhost:8848/nacos ,默认用户名密码均为 nacos,登录成功则表示 Nacos 安装成功。

2.2 安装与配置 Sentinel Dashboard

Sentinel 用于实现服务熔断与限流功能:

# 拉取镜像
docker pull bladex/sentinel-dashboard:1.8.7

# 启动容器
docker run -d \
  --name sentinel \
  -p 8080:8080 \
  bladex/sentinel-dashboard:1.8.7

访问 http://localhost:8080 ,默认用户名密码均为 sentinel。

2.3 安装 SkyWalking

SkyWalking 用于分布式链路追踪和性能分析:

# 拉取OAP服务镜像
docker pull apache/skywalking-oap-server:9.7.0

# 拉取UI镜像
docker pull apache/skywalking-ui:9.7.0

# 启动OAP服务
docker run -d \
  --name skywalking-oap \
  -p 11800:11800 \
  -p 12800:12800 \
  -e SW_STORAGE=h2 \
  apache/skywalking-oap-server:9.7.0

# 启动UI
docker run -d \
  --name skywalking-ui \
  -p 8081:8080 \
  -e SW_OAP_ADDRESS=http://skywalking-oap:12800 \
  --link skywalking-oap \
  apache/skywalking-ui:9.7.0

访问 http://localhost:8081 即可打开 SkyWalking 控制台。

2.4 安装 Prometheus 和 Grafana

# 拉取Prometheus镜像
docker pull prom/prometheus:v2.45.0

# 创建配置文件目录
mkdir -p /tmp/prometheus

# 创建配置文件
cat > /tmp/prometheus/prometheus.yml << EOF
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']
  - job_name: 'skywalking'
    static_configs:
      - targets: ['skywalking-oap:1234']
EOF

# 启动Prometheus
docker run -d \
  --name prometheus \
  -p 9090:9090 \
  -v /tmp/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml \
  --link skywalking-oap \
  prom/prometheus:v2.45.0

# 拉取Grafana镜像
docker pull grafana/grafana:10.4.2

# 启动Grafana
docker run -d \
  --name grafana \
  -p 3000:3000 \
  --link prometheus \
  grafana/grafana:10.4.2

访问 http://localhost:3000 ,默认用户名密码为 admin/admin。

三、构建基础微服务框架

3.1 创建父工程

首先创建一个 Maven 父工程,统一管理依赖版本:

<?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>

    <groupId>com.ken.microservice</groupId>
    <artifactId>spring-cloud-alibaba-demo</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <name>Spring Cloud Alibaba 实战示例</name>
    <description>Spring Cloud Alibaba 微服务从搭建到监控全链路示例</description>

    <!-- 子模块 -->
    <modules>
        <module>common</module>
        <module>user-service</module>
        <module>order-service</module>
        <module>product-service</module>
        <module>gateway-service</module>
    </modules>

    <!-- 版本管理 -->
    <properties>
        <java.version>17</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        
        <!-- Spring 版本 -->
        <spring.boot.version>3.2.6</spring.boot.version>
        <spring.cloud.version>2023.0.2</spring.cloud.version>
        <spring.cloud.alibaba.version>2023.0.1.0</spring.cloud.alibaba.version>
        
        <!-- 其他组件版本 -->
        <mybatis-plus.version>3.5.6</mybatis-plus.version>
        <seata.version>2.0.0</seata.version>
        <lombok.version>1.18.30</lombok.version>
        <fastjson2.version>2.0.47</fastjson2.version>
        <knife4j.version>4.4.0</knife4j.version>
        <mysql.version>8.0.36</mysql.version>
    </properties>

    <!-- 依赖管理 -->
    <dependencyManagement>
        <dependencies>
            <!-- Spring Boot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            
            <!-- Spring Cloud -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            
            <!-- Spring Cloud Alibaba -->
            <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>
            
            <!-- MyBatis-Plus -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            
            <!-- Seata -->
            <dependency>
                <groupId>io.seata</groupId>
                <artifactId>seata-spring-boot-starter</artifactId>
                <version>${seata.version}</version>
            </dependency>
            
            <!-- Lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
                <scope>provided</scope>
            </dependency>
            
            <!-- Fastjson2 -->
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2</artifactId>
                <version>${fastjson2.version}</version>
            </dependency>
            
            <!-- Knife4j (Swagger3) -->
            <dependency>
                <groupId>com.github.xiaoymin</groupId>
                <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
                <version>${knife4j.version}</version>
            </dependency>
            
            <!-- MySQL 驱动 -->
            <dependency>
                <groupId>com.mysql</groupId>
                <artifactId>mysql-connector-j</artifactId>
                <version>${mysql.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>${spring.boot.version}</version>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

3.2 创建公共模块

创建 common 模块,存放公共实体类、工具类等:

<?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.ken.microservice</groupId>
        <artifactId>spring-cloud-alibaba-demo</artifactId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>common</artifactId>
    <name>公共模块</name>
    <description>微服务公共组件</description>

    <dependencies>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        
        <!-- Fastjson2 -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
        </dependency>
        
        <!-- Spring 工具类 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <scope>provided</scope>
        </dependency>
        
        <!-- Google Guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>33.1.0-jre</version>
        </dependency>
        
        <!-- Swagger 注解 -->
        <dependency>
            <groupId>io.swagger.v3.oas</groupId>
            <artifactId>swagger-models</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

创建公共响应类:

package com.ken.microservice.common.result;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.util.ObjectUtils;

/**
 * 统一响应结果
 *
 * @param <T> 响应数据类型
 */
@Data
@Schema(description = "统一响应结果")
public class Result<T> {
    
    @Schema(description = "状态码", example = "200")
    private int code;
    
    @Schema(description = "消息", example = "操作成功")
    private String msg;
    
    @Schema(description = "响应数据")
    private T data;
    
    @JSONField(serialize = false)
    private boolean success;
    
    /**
     * 私有构造方法,防止直接实例化
     */
    private Result() {
    }
    
    /**
     * 成功响应
     *
     * @param <T> 数据类型
     * @return 成功响应结果
     */
    public static <T> Result<T> success() {
        return success(null);
    }
    
    /**
     * 成功响应
     *
     * @param data 响应数据
     * @param <T>  数据类型
     * @return 成功响应结果
     */
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(ResultCode.SUCCESS.getCode());
        result.setMsg(ResultCode.SUCCESS.getMsg());
        result.setData(data);
        result.setSuccess(true);
        return result;
    }
    
    /**
     * 失败响应
     *
     * @param code 错误码
     * @param msg  错误消息
     * @param <T>  数据类型
     * @return 失败响应结果
     */
    public static <T> Result<T> fail(int code, String msg) {
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(null);
        result.setSuccess(false);
        return result;
    }
    
    /**
     * 失败响应
     *
     * @param resultCode 错误码枚举
     * @param <T>        数据类型
     * @return 失败响应结果
     */
    public static <T> Result<T> fail(ResultCode resultCode) {
        return fail(resultCode.getCode(), resultCode.getMsg());
    }
    
    /**
     * 失败响应
     *
     * @param resultCode 错误码枚举
     * @param msg        错误消息
     * @param <T>        数据类型
     * @return 失败响应结果
     */
    public static <T> Result<T> fail(ResultCode resultCode, String msg) {
        return fail(resultCode.getCode(), msg);
    }
    
    /**
     * 转为JSON字符串
     *
     * @return JSON字符串
     */
    @Override
    public String toString() {
        return JSONObject.toJSONString(this);
    }
}

创建响应状态码枚举:

package com.ken.microservice.common.result;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 响应状态码枚举
 */
@Getter
@AllArgsConstructor
@Schema(description = "响应状态码枚举")
public enum ResultCode {
    
    /**
     * 成功
     */
    SUCCESS(200, "操作成功"),
    
    /**
     * 服务器内部错误
     */
    INTERNAL_SERVER_ERROR(500, "服务器内部错误"),
    
    /**
     * 请求参数错误
     */
    PARAM_ERROR(400, "请求参数错误"),
    
    /**
     * 未授权
     */
    UNAUTHORIZED(401, "未授权"),
    
    /**
     * 资源不存在
     */
    RESOURCE_NOT_FOUND(404, "资源不存在"),
    
    /**
     * 服务调用失败
     */
    SERVICE_CALL_FAILED(503, "服务调用失败");
    
    /**
     * 状态码
     */
    private final int code;
    
    /**
     * 状态描述
     */
    private final String msg;
}

四、用户服务实现

4.1 创建用户服务模块

<?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.ken.microservice</groupId>
        <artifactId>spring-cloud-alibaba-demo</artifactId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>user-service</artifactId>
    <name>用户服务</name>
    <description>用户管理微服务</description>

    <dependencies>
        <!-- 公共模块 -->
        <dependency>
            <groupId>com.ken.microservice</groupId>
            <artifactId>common</artifactId>
            <version>${project.version}</version>
        </dependency>
        
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- Spring Cloud Alibaba Nacos Discovery -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        
        <!-- Spring Cloud Alibaba Nacos Config -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        
        <!-- Spring Cloud Alibaba Sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        
        <!-- OpenFeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        
        <!-- MyBatis-Plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        
        <!-- MySQL 驱动 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
        </dependency>
        
        <!-- Knife4j (Swagger3) -->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
        </dependency>
        
        <!-- Seata -->
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

4.2 配置文件

创建 bootstrap.yml 配置文件:

spring:
  application:
    name: user-service
  profiles:
    active: dev
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        namespace: public
      config:
        server-addr: localhost:8848
        file-extension: yaml
        namespace: public
        group: DEFAULT_GROUP

创建 application-dev.yml 配置文件:

server:
  port: 8082

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/user_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: root

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.ken.microservice.user.entity
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

feign:
  sentinel:
    enabled: true

sentinel:
  transport:
    dashboard: localhost:8080
    port: 8719

seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: my_test_tx_group
  registry:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace:
      group: SEATA_GROUP
      application: seata-server
  config:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace:
      group: SEATA_GROUP

logging:
  level:
    com.ken.microservice.user: debug

# SkyWalking 配置
skywalking:
  agent:
    service_name: ${spring.application.name}
  collector:
    backend_service: localhost:11800

# Knife4j 配置
knife4j:
  enable: true
  openapi:
    title: 用户服务API
    description: 用户服务接口文档
    version: 1.0.0
    group:
      default:
        api-rule: package
        api-rule-resources:
          - com.ken.microservice.user.controller

4.3 数据库设计

创建用户数据库和表:

-- 创建数据库
CREATE DATABASE IF NOT EXISTS user_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 使用数据库
USE user_db;

-- 创建用户表
CREATE TABLE IF NOT EXISTS `user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(100) NOT NULL COMMENT '密码',
  `nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
  `phone` varchar(20) DEFAULT NULL COMMENT '手机号',
  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
  `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-禁用,1-正常',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';

-- 插入测试数据
INSERT INTO `user` (`username`, `password`, `nickname`, `phone`, `email`, `status`) 
VALUES 
('zhangsan', '123456', '张三', '13800138000', 'zhangsan@example.com', 1),
('lisi', '123456', '李四', '13900139000', 'lisi@example.com', 1);

4.4 实体类

package com.ken.microservice.user.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.time.LocalDateTime;

/**
 * 用户实体类
 */
@Data
@TableName("user")
@Schema(description = "用户实体")
public class User {
    
    @TableId(type = IdType.AUTO)
    @Schema(description = "用户ID", example = "1")
    private Long id;
    
    @Schema(description = "用户名", example = "zhangsan")
    private String username;
    
    @Schema(description = "密码", example = "123456")
    private String password;
    
    @Schema(description = "昵称", example = "张三")
    private String nickname;
    
    @Schema(description = "手机号", example = "13800138000")
    private String phone;
    
    @Schema(description = "邮箱", example = "zhangsan@example.com")
    private String email;
    
    @Schema(description = "状态:0-禁用,1-正常", example = "1")
    private Integer status;
    
    @Schema(description = "创建时间")
    private LocalDateTime createTime;
    
    @Schema(description = "更新时间")
    private LocalDateTime updateTime;
}

4.5 Mapper 接口

package com.ken.microservice.user.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ken.microservice.user.entity.User;
import org.apache.ibatis.annotations.Mapper;

/**
 * 用户Mapper接口
 */
@Mapper
public interface UserMapper extends BaseMapper<User> {
}

4.6 Service 层

package com.ken.microservice.user.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.ken.microservice.common.result.Result;
import com.ken.microservice.user.entity.User;

/**
 * 用户服务接口
 */
public interface UserService extends IService<User> {
    
    /**
     * 根据ID查询用户
     *
     * @param id 用户ID
     * @return 用户信息
     */
    Result<User> getUserById(Long id);
    
    /**
     * 根据用户名查询用户
     *
     * @param username 用户名
     * @return 用户信息
     */
    Result<User> getUserByUsername(String username);
    
    /**
     * 创建用户
     *
     * @param user 用户信息
     * @return 创建结果
     */
    Result<Long> createUser(User user);
    
    /**
     * 更新用户
     *
     * @param user 用户信息
     * @return 更新结果
     */
    Result<Boolean> updateUser(User user);
    
    /**
     * 删除用户
     *
     * @param id 用户ID
     * @return 删除结果
     */
    Result<Boolean> deleteUser(Long id);
}
package com.ken.microservice.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ken.microservice.common.result.Result;
import com.ken.microservice.common.result.ResultCode;
import com.ken.microservice.user.entity.User;
import com.ken.microservice.user.mapper.UserMapper;
import com.ken.microservice.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

/**
 * 用户服务实现类
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    @Override
    public Result<User> getUserById(Long id) {
        log.info("查询用户信息,用户ID:{}", id);
        
        if (id == null || id <= 0) {
            log.warn("查询用户信息失败,用户ID不合法:{}", id);
            return Result.fail(ResultCode.PARAM_ERROR, "用户ID不合法");
        }
        
        User user = baseMapper.selectById(id);
        if (user == null) {
            log.warn("查询用户信息失败,用户不存在,用户ID:{}", id);
            return Result.fail(ResultCode.RESOURCE_NOT_FOUND, "用户不存在");
        }
        
        log.info("查询用户信息成功,用户ID:{}", id);
        return Result.success(user);
    }
    
    @Override
    public Result<User> getUserByUsername(String username) {
        log.info("查询用户信息,用户名:{}", username);
        
        if (!StringUtils.hasText(username)) {
            log.warn("查询用户信息失败,用户名为空");
            return Result.fail(ResultCode.PARAM_ERROR, "用户名不能为空");
        }
        
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, username);
        
        User user = baseMapper.selectOne(queryWrapper);
        if (user == null) {
            log.warn("查询用户信息失败,用户不存在,用户名:{}", username);
            return Result.fail(ResultCode.RESOURCE_NOT_FOUND, "用户不存在");
        }
        
        log.info("查询用户信息成功,用户名:{}", username);
        return Result.success(user);
    }
    
    @Override
    public Result<Long> createUser(User user) {
        log.info("创建用户,用户信息:{}", user);
        
        if (user == null) {
            log.warn("创建用户失败,用户信息为空");
            return Result.fail(ResultCode.PARAM_ERROR, "用户信息不能为空");
        }
        
        if (!StringUtils.hasText(user.getUsername())) {
            log.warn("创建用户失败,用户名为空");
            return Result.fail(ResultCode.PARAM_ERROR, "用户名不能为空");
        }
        
        if (!StringUtils.hasText(user.getPassword())) {
            log.warn("创建用户失败,密码为空");
            return Result.fail(ResultCode.PARAM_ERROR, "密码不能为空");
        }
        
        // 检查用户名是否已存在
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, user.getUsername());
        long count = baseMapper.selectCount(queryWrapper);
        if (count > 0) {
            log.warn("创建用户失败,用户名已存在:{}", user.getUsername());
            return Result.fail(ResultCode.PARAM_ERROR, "用户名已存在");
        }
        
        int rows = baseMapper.insert(user);
        if (rows <= 0) {
            log.error("创建用户失败,数据库操作失败");
            return Result.fail(ResultCode.INTERNAL_SERVER_ERROR, "创建用户失败");
        }
        
        log.info("创建用户成功,用户ID:{}", user.getId());
        return Result.success(user.getId());
    }
    
    @Override
    public Result<Boolean> updateUser(User user) {
        log.info("更新用户,用户信息:{}", user);
        
        if (user == null || user.getId() == null) {
            log.warn("更新用户失败,用户ID为空");
            return Result.fail(ResultCode.PARAM_ERROR, "用户ID不能为空");
        }
        
        // 检查用户是否存在
        User existingUser = baseMapper.selectById(user.getId());
        if (existingUser == null) {
            log.warn("更新用户失败,用户不存在,用户ID:{}", user.getId());
            return Result.fail(ResultCode.RESOURCE_NOT_FOUND, "用户不存在");
        }
        
        int rows = baseMapper.updateById(user);
        if (rows <= 0) {
            log.error("更新用户失败,数据库操作失败,用户ID:{}", user.getId());
            return Result.fail(ResultCode.INTERNAL_SERVER_ERROR, "更新用户失败");
        }
        
        log.info("更新用户成功,用户ID:{}", user.getId());
        return Result.success(true);
    }
    
    @Override
    public Result<Boolean> deleteUser(Long id) {
        log.info("删除用户,用户ID:{}", id);
        
        if (id == null || id <= 0) {
            log.warn("删除用户失败,用户ID不合法:{}", id);
            return Result.fail(ResultCode.PARAM_ERROR, "用户ID不合法");
        }
        
        // 检查用户是否存在
        User user = baseMapper.selectById(id);
        if (user == null) {
            log.warn("删除用户失败,用户不存在,用户ID:{}", id);
            return Result.fail(ResultCode.RESOURCE_NOT_FOUND, "用户不存在");
        }
        
        int rows = baseMapper.deleteById(id);
        if (rows <= 0) {
            log.error("删除用户失败,数据库操作失败,用户ID:{}", id);
            return Result.fail(ResultCode.INTERNAL_SERVER_ERROR, "删除用户失败");
        }
        
        log.info("删除用户成功,用户ID:{}", id);
        return Result.success(true);
    }
}

4.7 Controller 层

package com.ken.microservice.user.controller;

import com.ken.microservice.common.result.Result;
import com.ken.microservice.user.entity.User;
import com.ken.microservice.user.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

/**
 * 用户控制器
 */
@Slf4j
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
@Tag(name = "用户管理", description = "用户CRUD接口")
public class UserController {
    
    private final UserService userService;
    
    @Operation(summary = "根据ID查询用户", description = "通过用户ID获取用户详细信息")
    @GetMapping("/{id}")
    public Result<User> getUserById(
            @Parameter(description = "用户ID", required = true, example = "1")
            @PathVariable Long id) {
        return userService.getUserById(id);
    }
    
    @Operation(summary = "根据用户名查询用户", description = "通过用户名获取用户详细信息")
    @GetMapping("/username/{username}")
    public Result<User> getUserByUsername(
            @Parameter(description = "用户名", required = true, example = "zhangsan")
            @PathVariable String username) {
        return userService.getUserByUsername(username);
    }
    
    @Operation(summary = "创建用户", description = "新增用户信息")
    @PostMapping
    public Result<Long> createUser(
            @Parameter(description = "用户信息", required = true)
            @RequestBody User user) {
        return userService.createUser(user);
    }
    
    @Operation(summary = "更新用户", description = "修改用户信息")
    @PutMapping
    public Result<Boolean> updateUser(
            @Parameter(description = "用户信息", required = true)
            @RequestBody User user) {
        return userService.updateUser(user);
    }
    
    @Operation(summary = "删除用户", description = "根据ID删除用户")
    @DeleteMapping("/{id}")
    public Result<Boolean> deleteUser(
            @Parameter(description = "用户ID", required = true, example = "1")
            @PathVariable Long id) {
        return userService.deleteUser(id);
    }
}

4.8 启动类

package com.ken.microservice.user;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * 用户服务启动类
 */
@SpringBootApplication(scanBasePackages = "com.ken.microservice")
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.ken.microservice")
@MapperScan("com.ken.microservice.user.mapper")
public class UserServiceApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

五、商品服务实现

商品服务的实现与用户服务类似,这里重点展示核心代码:

5.1 数据库设计

-- 创建数据库
CREATE DATABASE IF NOT EXISTS product_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 使用数据库
USE product_db;

-- 创建商品表
CREATE TABLE IF NOT EXISTS `product` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品ID',
  `name` varchar(100) NOT NULL COMMENT '商品名称',
  `price` decimal(10,2) NOT NULL COMMENT '商品价格',
  `stock` int NOT NULL DEFAULT 0 COMMENT '商品库存',
  `description` varchar(500) DEFAULT NULL COMMENT '商品描述',
  `status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:0-下架,1-上架',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='商品表';

-- 插入测试数据
INSERT INTO `product` (`name`, `price`, `stock`, `description`, `status`) 
VALUES 
('iPhone 15', 7999.00, 100, '苹果手机iPhone 15', 1),
('华为Mate 60', 6999.00, 50, '华为手机Mate 60', 1),
('小米14', 4999.00, 200, '小米手机14', 1);

5.2 核心业务代码

实体类:

package com.ken.microservice.product.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 商品实体类
 */
@Data
@TableName("product")
@Schema(description = "商品实体")
public class Product {
    
    @TableId(type = IdType.AUTO)
    @Schema(description = "商品ID", example = "1")
    private Long id;
    
    @Schema(description = "商品名称", example = "iPhone 15")
    private String name;
    
    @Schema(description = "商品价格", example = "7999.00")
    private BigDecimal price;
    
    @Schema(description = "商品库存", example = "100")
    private Integer stock;
    
    @Schema(description = "商品描述", example = "苹果手机iPhone 15")
    private String description;
    
    @Schema(description = "状态:0-下架,1-上架", example = "1")
    private Integer status;
    
    @Schema(description = "创建时间")
    private LocalDateTime createTime;
    
    @Schema(description = "更新时间")
    private LocalDateTime updateTime;
}

服务实现类中扣减库存的方法:

/**
 * 扣减商品库存
 *
 * @param productId 商品ID
 * @param quantity  扣减数量
 * @return 扣减结果
 */
@Override
public Result<Boolean> decreaseStock(Long productId, Integer quantity) {
    log.info("扣减商品库存,商品ID:{},扣减数量:{}", productId, quantity);
    
    if (productId == null || productId <= 0) {
        log.warn("扣减商品库存失败,商品ID不合法:{}", productId);
        return Result.fail(ResultCode.PARAM_ERROR, "商品ID不合法");
    }
    
    if (quantity == null || quantity <= 0) {
        log.warn("扣减商品库存失败,扣减数量不合法:{}", quantity);
        return Result.fail(ResultCode.PARAM_ERROR, "扣减数量不合法");
    }
    
    // 查询商品信息
    Product product = baseMapper.selectById(productId);
    if (product == null) {
        log.warn("扣减商品库存失败,商品不存在,商品ID:{}", productId);
        return Result.fail(ResultCode.RESOURCE_NOT_FOUND, "商品不存在");
    }
    
    // 检查库存是否充足
    if (product.getStock() < quantity) {
        log.warn("扣减商品库存失败,库存不足,商品ID:{},当前库存:{},需要数量:{}", 
                productId, product.getStock(), quantity);
        return Result.fail(ResultCode.PARAM_ERROR, "商品库存不足");
    }
    
    // 扣减库存
    Product updateProduct = new Product();
    updateProduct.setId(productId);
    updateProduct.setStock(product.getStock() - quantity);
    
    int rows = baseMapper.updateById(updateProduct);
    if (rows <= 0) {
        log.error("扣减商品库存失败,数据库操作失败,商品ID:{}", productId);
        return Result.fail(ResultCode.INTERNAL_SERVER_ERROR, "扣减库存失败");
    }
    
    log.info("扣减商品库存成功,商品ID:{},扣减数量:{},剩余库存:{}", 
            productId, quantity, updateProduct.getStock());
    return Result.success(true);
}

六、订单服务实现

订单服务需要调用用户服务和商品服务,这里重点展示服务调用和分布式事务相关代码:

6.1 数据库设计

-- 创建数据库
CREATE DATABASE IF NOT EXISTS order_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 使用数据库
USE order_db;

-- 创建订单表
CREATE TABLE IF NOT EXISTS `order` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  `order_no` varchar(50) NOT NULL COMMENT '订单编号',
  `user_id` bigint NOT NULL COMMENT '用户ID',
  `total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额',
  `status` tinyint NOT NULL DEFAULT 0 COMMENT '订单状态:0-待支付,1-已支付,2-已取消,3-已完成',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_order_no` (`order_no`),
  KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单表';

-- 创建订单项表
CREATE TABLE IF NOT EXISTS `order_item` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '订单项ID',
  `order_id` bigint NOT NULL COMMENT '订单ID',
  `product_id` bigint NOT NULL COMMENT '商品ID',
  `product_name` varchar(100) NOT NULL COMMENT '商品名称',
  `product_price` decimal(10,2) NOT NULL COMMENT '商品单价',
  `quantity` int NOT NULL COMMENT '购买数量',
  `total_price` decimal(10,2) NOT NULL COMMENT '商品总价',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_order_id` (`order_id`),
  KEY `idx_product_id` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单项表';

-- 创建undo_log表(Seata分布式事务需要)
CREATE TABLE IF NOT EXISTS `undo_log` (
  `branch_id` bigint NOT NULL COMMENT 'branch transaction id',
  `xid` varchar(100) NOT NULL COMMENT 'global transaction id',
  `context` varchar(128) NOT NULL COMMENT 'undo_log context,such as serialization',
  `rollback_info` longblob NOT NULL COMMENT 'rollback info',
  `log_status` int NOT NULL COMMENT '0:normal status,1:defense status',
  `log_created` datetime(6) NOT NULL COMMENT 'create datetime',
  `log_modified` datetime(6) NOT NULL COMMENT 'modify datetime',
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='AT transaction mode undo table';

6.2 Feign 客户端

创建用户服务 Feign 客户端:

package com.ken.microservice.order.feign;

import com.ken.microservice.common.result.Result;
import com.ken.microservice.user.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * 用户服务Feign客户端
 */
@FeignClient(name = "user-service", fallback = UserFeignClientFallback.class)
public interface UserFeignClient {
    
    /**
     * 根据ID查询用户
     *
     * @param id 用户ID
     * @return 用户信息
     */
    @GetMapping("/api/v1/users/{id}")
    Result<User> getUserById(@PathVariable("id") Long id);
}

创建商品服务 Feign 客户端:

package com.ken.microservice.order.feign;

import com.ken.microservice.common.result.Result;
import com.ken.microservice.product.entity.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * 商品服务Feign客户端
 */
@FeignClient(name = "product-service", fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {
    
    /**
     * 根据ID查询商品
     *
     * @param id 商品ID
     * @return 商品信息
     */
    @GetMapping("/api/v1/products/{id}")
    Result<Product> getProductById(@PathVariable("id") Long id);
    
    /**
     * 扣减商品库存
     *
     * @param productId 商品ID
     * @param quantity  扣减数量
     * @return 扣减结果
     */
    @PostMapping("/api/v1/products/decrease-stock")
    Result<Boolean> decreaseStock(
            @RequestParam("productId") Long productId,
            @RequestParam("quantity") Integer quantity);
}

创建 Feign 客户端降级类:

package com.ken.microservice.order.feign;

import com.ken.microservice.common.result.Result;
import com.ken.microservice.common.result.ResultCode;
import com.ken.microservice.user.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 用户服务Feign客户端降级类
 */
@Slf4j
@Component
public class UserFeignClientFallback implements UserFeignClient {
    
    @Override
    public Result<User> getUserById(Long id) {
        log.error("调用用户服务失败,用户ID:{},执行降级处理", id);
        return Result.fail(ResultCode.SERVICE_CALL_FAILED, "调用用户服务失败,请稍后重试");
    }
}

6.3 订单服务核心代码

订单实体类:

package com.ken.microservice.order.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 订单实体类
 */
@Data
@TableName("order")
@Schema(description = "订单实体")
public class Order {
    
    @TableId(type = IdType.AUTO)
    @Schema(description = "订单ID", example = "1")
    private Long id;
    
    @Schema(description = "订单编号", example = "202306010001")
    private String orderNo;
    
    @Schema(description = "用户ID", example = "1")
    private Long userId;
    
    @Schema(description = "订单总金额", example = "7999.00")
    private BigDecimal totalAmount;
    
    @Schema(description = "订单状态:0-待支付,1-已支付,2-已取消,3-已完成", example = "0")
    private Integer status;
    
    @Schema(description = "创建时间")
    private LocalDateTime createTime;
    
    @Schema(description = "更新时间")
    private LocalDateTime updateTime;
}

订单项实体

package com.ken.microservice.order.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 订单项实体类
 */
@Data
@TableName("order_item")
@Schema(description = "订单项实体")
public class OrderItem {
    
    @TableId(type = IdType.AUTO)
    @Schema(description = "订单项ID", example = "1")
    private Long id;
    
    @Schema(description = "订单ID", example = "1")
    private Long orderId;
    
    @Schema(description = "商品ID", example = "1")
    private Long productId;
    
    @Schema(description = "商品名称", example = "iPhone 15")
    private String productName;
    
    @Schema(description = "商品单价", example = "7999.00")
    private BigDecimal productPrice;
    
    @Schema(description = "购买数量", example = "1")
    private Integer quantity;
    
    @Schema(description = "商品总价", example = "7999.00")
    private BigDecimal totalPrice;
    
    @Schema(description = "创建时间")
    private LocalDateTime createTime;
    
    @Schema(description = "更新时间")
    private LocalDateTime updateTime;
}

创建订单请求 DTO:

package com.ken.microservice.order.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;

/**
 * 创建订单请求DTO
 */
@Data
@Schema(description = "创建订单请求DTO")
public class CreateOrderDTO {
    
    @Schema(description = "用户ID", required = true, example = "1")
    private Long userId;
    
    @Schema(description = "订单项列表", required = true)
    private List<OrderItemDTO> orderItems;
    
    /**
     * 校验请求参数
     *
     * @return 校验结果
     */
    public String validate() {
        if (userId == null || userId <= 0) {
            return "用户ID不合法";
        }
        
        if (CollectionUtils.isEmpty(orderItems)) {
            return "订单项不能为空";
        }
        
        for (OrderItemDTO item : orderItems) {
            if (item.getProductId() == null || item.getProductId() <= 0) {
                return "商品ID不合法";
            }
            
            if (item.getQuantity() == null || item.getQuantity() <= 0) {
                return "购买数量不合法";
            }
        }
        
        return null;
    }
}

/**
 * 订单项DTO
 */
@Data
@Schema(description = "订单项DTO")
class OrderItemDTO {
    
    @Schema(description = "商品ID", required = true, example = "1")
    private Long productId;
    
    @Schema(description = "购买数量", required = true, example = "1")
    private Integer quantity;
}

订单服务实现类(包含分布式事务):

package com.ken.microservice.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ken.microservice.common.result.Result;
import com.ken.microservice.common.result.ResultCode;
import com.ken.microservice.order.dto.CreateOrderDTO;
import com.ken.microservice.order.entity.Order;
import com.ken.microservice.order.entity.OrderItem;
import com.ken.microservice.order.feign.ProductFeignClient;
import com.ken.microservice.order.feign.UserFeignClient;
import com.ken.microservice.order.mapper.OrderItemMapper;
import com.ken.microservice.order.mapper.OrderMapper;
import com.ken.microservice.order.service.OrderService;
import com.ken.microservice.product.entity.Product;
import com.ken.microservice.user.entity.User;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 订单服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    
    private final OrderMapper orderMapper;
    private final OrderItemMapper orderItemMapper;
    private final UserFeignClient userFeignClient;
    private final ProductFeignClient productFeignClient;
    
    /**
     * 创建订单
     * 使用Seata的全局事务注解保证分布式事务一致性
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class) // Seata全局事务注解
    public Result<String> createOrder(CreateOrderDTO createOrderDTO) {
        log.info("开始创建订单,请求参数:{}", createOrderDTO);
        
        // 校验请求参数
        String validateResult = createOrderDTO.validate();
        if (validateResult != null) {
            log.warn("创建订单失败,参数校验失败:{}", validateResult);
            return Result.fail(ResultCode.PARAM_ERROR, validateResult);
        }
        
        Long userId = createOrderDTO.getUserId();
        
        // 1. 查询用户信息
        Result<User> userResult = userFeignClient.getUserById(userId);
        if (!userResult.isSuccess()) {
            log.error("创建订单失败,查询用户信息失败:{}", userResult.getMsg());
            return Result.fail(ResultCode.SERVICE_CALL_FAILED, "查询用户信息失败:" + userResult.getMsg());
        }
        
        // 2. 查询商品信息并计算总金额
        List<OrderItem> orderItems = new ArrayList<>();
        BigDecimal totalAmount = BigDecimal.ZERO;
        
        for (CreateOrderDTO.OrderItemDTO itemDTO : createOrderDTO.getOrderItems()) {
            Long productId = itemDTO.getProductId();
            Integer quantity = itemDTO.getQuantity();
            
            // 查询商品信息
            Result<Product> productResult = productFeignClient.getProductById(productId);
            if (!productResult.isSuccess()) {
                log.error("创建订单失败,查询商品信息失败,商品ID:{},错误信息:{}", 
                        productId, productResult.getMsg());
                throw new RuntimeException("查询商品信息失败:" + productResult.getMsg());
            }
            
            Product product = productResult.getData();
            
            // 检查商品状态
            if (product.getStatus() != 1) {
                log.error("创建订单失败,商品未上架,商品ID:{},商品名称:{}", 
                        productId, product.getName());
                throw new RuntimeException("商品[" + product.getName() + "]未上架");
            }
            
            // 检查库存
            if (product.getStock() < quantity) {
                log.error("创建订单失败,商品库存不足,商品ID:{},商品名称:{},当前库存:{},需要数量:{}", 
                        productId, product.getName(), product.getStock(), quantity);
                throw new RuntimeException("商品[" + product.getName() + "]库存不足");
            }
            
            // 扣减库存
            Result<Boolean> decreaseResult = productFeignClient.decreaseStock(productId, quantity);
            if (!decreaseResult.isSuccess()) {
                log.error("创建订单失败,扣减商品库存失败,商品ID:{},错误信息:{}", 
                        productId, decreaseResult.getMsg());
                throw new RuntimeException("扣减商品[" + product.getName() + "]库存失败:" + decreaseResult.getMsg());
            }
            
            // 计算商品总价
            BigDecimal itemTotalPrice = product.getPrice().multiply(new BigDecimal(quantity));
            totalAmount = totalAmount.add(itemTotalPrice);
            
            // 创建订单项
            OrderItem orderItem = new OrderItem();
            orderItem.setProductId(productId);
            orderItem.setProductName(product.getName());
            orderItem.setProductPrice(product.getPrice());
            orderItem.setQuantity(quantity);
            orderItem.setTotalPrice(itemTotalPrice);
            orderItem.setCreateTime(LocalDateTime.now());
            orderItem.setUpdateTime(LocalDateTime.now());
            
            orderItems.add(orderItem);
        }
        
        // 3. 创建订单
        Order order = new Order();
        // 生成订单编号:年月日时分秒 + 3位随机数
        String orderNo = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) 
                + (100 + new Random().nextInt(900));
        order.setOrderNo(orderNo);
        order.setUserId(userId);
        order.setTotalAmount(totalAmount);
        order.setStatus(0); // 待支付
        order.setCreateTime(LocalDateTime.now());
        order.setUpdateTime(LocalDateTime.now());
        
        int orderRows = orderMapper.insert(order);
        if (orderRows <= 0) {
            log.error("创建订单失败,保存订单信息到数据库失败");
            throw new RuntimeException("创建订单失败");
        }
        
        // 4. 保存订单项
        for (OrderItem item : orderItems) {
            item.setOrderId(order.getId());
            orderItemMapper.insert(item);
        }
        
        log.info("创建订单成功,订单编号:{},订单ID:{}", orderNo, order.getId());
        return Result.success(orderNo);
    }
    
    // 其他方法省略...
}

七、网关服务实现

Spring Cloud Gateway 作为微服务网关,负责路由转发、负载均衡、认证授权等功能。

7.1 网关模块配置

<?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.ken.microservice</groupId>
        <artifactId>spring-cloud-alibaba-demo</artifactId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gateway-service</artifactId>
    <name>网关服务</name>
    <description>微服务网关</description>

    <dependencies>
        <!-- 公共模块 -->
        <dependency>
            <groupId>com.ken.microservice</groupId>
            <artifactId>common</artifactId>
            <version>${project.version}</version>
        </dependency>
        
        <!-- Spring Cloud Gateway -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        
        <!-- Spring Cloud Alibaba Nacos Discovery -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        
        <!-- Spring Cloud Alibaba Nacos Config -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        
        <!-- Spring Cloud Alibaba Sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        
        <!-- Spring Cloud Gateway Sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>
        
        <!-- Knife4j Gateway -->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-gateway-spring-boot-starter</artifactId>
            <version>${knife4j.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

7.2 网关配置文件

spring:
  application:
    name: gateway-service
  profiles:
    active: dev
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        namespace: public
      config:
        server-addr: localhost:8848
        file-extension: yaml
        namespace: public
        group: DEFAULT_GROUP
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
      routes:
        # 用户服务路由
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/v1/users/**filters:
            - name: Sentinel
        # 商品服务路由
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/api/v1/products/**
          filters:
            - name: Sentinel
        # 订单服务路由
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/v1/orders/**
          filters:
            - name: Sentinel
        # Swagger路由
        - id: swagger-route
          uri: lb://user-service
          predicates:
            - Path=/swagger-ui.html,/swagger-ui/**,/v3/api-docs/**,/doc.html/**
          filters:
            - name: RewritePath
              args:
                regexp: /doc.html/(?<segment>.*)
                replacement: /$\{segment}

server:
  port: 8080

sentinel:
  transport:
    dashboard: localhost:8080
    port: 8720
  scg:
    fallback:
      mode: response
      response-status: 429
      response-body: '{"code":429,"msg":"请求过于频繁,请稍后再试","data":null,"success":false}'

# Knife4j 配置
knife4j:
  gateway:
    enabled: true
    routes:
      - name: 用户服务
        url: /user-service/v3/api-docs
        service-name: user-service
      - name: 商品服务
        url: /product-service/v3/api-docs
        service-name: product-service
      - name: 订单服务
        url: /order-service/v3/api-docs
        service-name: order-service

logging:
  level:
    org.springframework.cloud.gateway: debug
    com.alibaba.cloud.sentinel: debug

7.3 网关启动类

package com.ken.microservice.gateway;

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

/**
 * 网关服务启动类
 */
@SpringBootApplication(scanBasePackages = "com.ken.microservice")
@EnableDiscoveryClient
public class GatewayServiceApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(GatewayServiceApplication.class, args);
    }
}

八、服务监控与可观测性

8.1 集成 SkyWalking 实现链路追踪

在各服务的启动参数中添加 SkyWalking agent 配置:

-javaagent:/path/to/skywalking-agent.jar
-Dskywalking.agent.service_name=user-service
-Dskywalking.collector.backend_service=localhost:11800

通过 SkyWalking 控制台,我们可以查看完整的服务调用链路:

8.2 集成 Prometheus 和 Grafana 实现指标监控

在各服务中添加 Prometheus 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

添加配置:

management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus
  metrics:
    export:
      prometheus:
        enabled: true
  endpoint:
    health:
      show-details: always
    prometheus:
      enabled: true

在 Prometheus 配置文件中添加各服务的监控端点:

scrape_configs:
  - job_name: 'user-service'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['user-service:8082']
  
  - job_name: 'product-service'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['product-service:8083']
  
  - job_name: 'order-service'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['order-service:8084']
  
  - job_name: 'gateway-service'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['gateway-service:8080']

在 Grafana 中导入 Spring Boot 应用的监控面板(ID:12856),可以直观地查看各服务的 JVM、请求量、响应时间等指标。

8.3 集成 Sentinel 实现熔断与限流

在 Sentinel 控制台中,可以为各服务设置限流规则:

  1. 为用户服务的查询接口设置 QPS 限流为 10:

    • 资源名:GET:/api/v1/users/{id}
    • 限流类型:QPS
    • 阈值:10
  2. 为订单服务的创建订单接口设置线程数限流为 5:

    • 资源名:POST:/api/v1/orders
    • 限流类型:线程数
    • 阈值:5
  3. 为商品服务的扣减库存接口设置熔断规则:

    • 资源名:POST:/api/v1/products/decrease-stock
    • 熔断策略:异常比例
    • 阈值:0.5
    • 熔断时长:10 秒
    • 最小请求数:5

九、微服务高级特性

9.1 配置中心动态配置

在 Nacos 控制台中创建配置:

  • 数据 ID:user-service-dev.yaml
  • 配置内容:
user:
  config:
    version: 1.0.0
    enableCache: true
    cacheExpireSeconds: 300

在用户服务中添加配置类:

package com.ken.microservice.user.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

/**
 * 用户服务配置
 */
@Data
@Component
@ConfigurationProperties(prefix = "user.config")
@RefreshScope // 支持配置动态刷新
public class UserConfig {
    
    private String version;
    
    private boolean enableCache;
    
    private Integer cacheExpireSeconds;
}

9.2 服务优雅降级与容错

使用 Resilience4j 实现服务容错:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>

在服务调用处添加熔断注解:

@CircuitBreaker(name = "productService", fallbackMethod = "getProductFallback")
public Result<Product> getProduct(Long productId) {
    return productFeignClient.getProductById(productId);
}

public Result<Product> getProductFallback(Long productId, Exception e) {
    log.error("调用商品服务失败,执行降级处理,商品ID:{}", productId, e);
    return Result.fail(ResultCode.SERVICE_CALL_FAILED, "获取商品信息失败,请稍后重试");
}

十、总结与展望

本文详细介绍了基于 Spring Cloud Alibaba 的微服务从搭建到监控的全链路实践,涵盖了服务注册发现、配置中心、服务调用、熔断限流、网关路由、分布式事务、链路追踪、指标监控等核心功能。通过实际代码示例,展示了如何构建一个完整的微服务体系。

附录:常见问题与解决方案

  1. Nacos 服务注册失败

    • 检查 Nacos 服务器是否正常运行
    • 检查服务配置的 Nacos 地址是否正确
    • 检查网络是否通畅,防火墙是否开放对应端口
  2. Feign 调用失败

    • 检查服务是否已注册到注册中心
    • 检查 Feign 接口定义是否与服务端接口一致
    • 检查熔断降级配置是否正确
  3. 分布式事务不生效

    • 检查 Seata 服务器是否正常运行
    • 检查各服务是否正确配置了 Seata
    • 检查数据库是否创建了 undo_log 表
    • 确保 @GlobalTransactional 注解正确添加到事务发起方
  4. SkyWalking 链路追踪不显示

    • 检查 SkyWalking agent 是否正确配置
    • 检查服务启动参数是否正确
    • 检查 SkyWalking OAP 服务是否正常运行
  5. Sentinel 规则不生效

    • 检查 Sentinel Dashboard 是否正常运行
    • 检查服务是否正确配置了 Sentinel
    • 确保服务已被调用过,Sentinel 已收集到服务信息