【基于 Spring Cloud Alibaba 的微服务电商项目】完整实现思路

文章目录

基于Spring Cloud Alibaba的微服务电商项目全流程实战(高并发+高可用+一致性保障)

若对您有帮助的话,请点赞收藏加关注哦,您的关注是我持续创作的动力!有问题请私信或联系邮箱:funian.gm@gmail.com

一、需求分析

1. 业务需求

核心模块 核心功能
用户服务 注册/登录(JWT鉴权)、用户信息管理、地址管理
商品服务 商品CRUD、分类管理、商品上下架、库存关联
订单服务 订单创建、订单状态流转、订单查询、超时取消
库存服务 库存扣减、库存锁定、库存回滚、防超卖
支付服务 对接支付宝/微信支付、支付回调、退款处理
秒杀服务 秒杀商品发布、限流削峰、异步下单
网关服务 路由转发、限流熔断、统一鉴权、日志收集

2. 技术需求

  • 高并发:支持秒杀场景10万QPS,普通下单1万QPS
  • 高可用:服务可用性99.99%,核心链路无单点故障
  • 一致性:分布式事务保证订单-库存-支付数据一致
  • 可扩展:支持水平扩容,应对流量峰值
  • 安全性:接口幂等性、防SQL注入、防缓存穿透/击穿/雪崩
  • 可监控:全链路监控、告警机制(响应时间>3s告警)

二、数据库表设计(按服务拆分)

1. 数据库拆分原则

  • 每个微服务独立数据库,避免跨库联查
  • 核心表设计冗余字段(如订单表存商品名称/价格,避免查商品库)
  • 分库分表:订单表、商品表按规则拆分(Sharding-JDBC)

2. 核心表结构(MySQL)

(1)用户服务数据库(user_db)
表名 核心字段 索引设计
user id(PK)、username、password(加密)、phone、status username(唯一)、phone(唯一)
user_address id(PK)、user_id(FK)、receiver、phone、address user_id(普通索引)
(2)商品服务数据库(product_db)
表名 核心字段 索引设计
product id(PK)、name、price、stock、status、category_id category_id(普通)、name(模糊索引)
product_category id(PK)、name、parent_id、level parent_id(普通)
(3)订单服务数据库(order_db)
表名 核心字段 索引设计
order_main id(PK)、order_sn(订单号)、user_id、total_amount、status、create_time order_sn(唯一)、user_id(普通)、create_time(普通)
order_item id(PK)、order_id(FK)、product_id、product_name、price、quantity order_id(普通)、product_id(普通)
message_queue id(PK)、order_id、message、status(0未发送1已发送)、create_time order_id(普通)、status(普通)
(4)库存服务数据库(inventory_db)
表名 核心字段 索引设计
inventory id(PK)、product_id、stock、locked_stock(锁定库存) product_id(唯一)
inventory_log id(PK)、product_id、operate_type(1扣减2回滚)、quantity、order_id product_id(普通)、order_id(普通)
(5)支付服务数据库(pay_db)
表名 核心字段 索引设计
payment id(PK)、order_sn、user_id、pay_amount、pay_type(1支付宝2微信)、status order_sn(唯一)、user_id(普通)
refund id(PK)、payment_id(FK)、refund_amount、status、refund_time payment_id(普通)

三、技术栈选型(Spring Cloud Alibaba生态)

技术分层 选型组件 核心作用
微服务框架 Spring Cloud Alibaba 微服务生态核心(整合各类组件)
注册中心 Nacos 服务注册与发现(支持健康检查)
配置中心 Nacos Config 动态配置、环境隔离(dev/test/prod)
网关 Sentinel Gateway 路由转发、限流、熔断、鉴权
RPC通信 Dubbo 高性能RPC调用(替代OpenFeign,支持负载均衡)
熔断降级 Sentinel 服务熔断、降级、限流(支持注解式配置)
分布式事务 Seata 保证订单-库存-支付一致性(TCC/AT模式)
缓存 Redis(集群) 商品缓存、分布式锁、会话缓存
消息队列 RocketMQ 异步通信、延迟队列(订单超时)、削峰
数据库 MySQL(主从复制) 业务数据存储(主写从读)
分库分表 Sharding-JDBC 订单表/商品表水平拆分(应对大数据量)
分布式锁 Redisson 库存扣减防超卖、秒杀锁
鉴权 JWT 无状态登录鉴权(替代Session)
监控 Prometheus+Grafana 全链路指标监控(QPS/响应时间/错误率)
日志 ELK(Elasticsearch+Logstash+Kibana) 日志收集与分析
部署 Docker+K8s 容器化部署、自动扩缩容

四、微服务架构设计

1. 架构分层(从上到下)

复制代码
客户端层(APP/小程序/PC)→ CDN(静态资源加速)→ 网关层(Sentinel Gateway)→ 微服务层 → 中间件层 → 基础设施层

2. 服务拆分原则

  • 单一职责:每个服务只负责一个核心领域(如订单服务只处理订单相关)
  • 高内聚低耦合:服务内部逻辑紧密,服务间通过接口通信
  • 数据自治:每个服务独立管理自己的数据库,不跨库操作
  • 可独立部署:服务之间无依赖,支持单独扩容/升级

3. 核心链路调用流程

复制代码
下单流程:用户→网关→订单服务(创建订单)→ Dubbo调用库存服务(预扣库存)→ Dubbo调用支付服务(发起支付)→ 支付回调→订单服务(更新状态)→ 库存服务(确认扣减)

五、核心微服务开发实现

1. 通用基础配置(所有服务依赖)

(1)pom.xml核心依赖(Spring Boot 2.7.x + Spring Cloud Alibaba 2021.0.4.0)
xml 复制代码
<!-- Spring Cloud Alibaba 核心依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2021.0.4.0</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
<!-- Nacos 注册中心 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Nacos 配置中心 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Dubbo RPC -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<!-- Sentinel 熔断降级 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- Seata 分布式事务 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
(2)bootstrap.yml(连接Nacos配置中心)
yaml 复制代码
spring:
  application:
    name: order-service # 服务名(对应Nacos配置)
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.100:8848 # Nacos注册中心地址
      config:
        server-addr: 192.168.1.100:8848 # Nacos配置中心地址
        file-extension: yaml # 配置文件格式
        namespace: dev # 环境隔离(dev/test/prod)
        group: ORDER_GROUP # 配置分组(按服务分组)
dubbo:
  registry:
    address: nacos://192.168.1.100:8848 # Dubbo基于Nacos服务发现
  protocol:
    name: dubbo
    port: -1 # 随机端口

2. 用户服务(user-service)

(1)核心功能:JWT鉴权
java 复制代码
// JWT工具类
@Component
public class JwtUtil {
    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expire}")
    private long expire;

    // 生成Token
    public String generateToken(Long userId) {
        Date now = new Date();
        Date expireDate = new Date(now.getTime() + expire * 1000);
        return Jwts.builder()
                .setSubject(userId.toString())
                .setIssuedAt(now)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    // 验证Token
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    // 从Token中获取用户ID
    public Long getUserIdFromToken(String token) {
        Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        return Long.parseLong(claims.getSubject());
    }
}
(2)登录接口
java 复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/login")
    public Result<String> login(@RequestBody LoginDTO loginDTO) {
        // 验证用户名密码(省略加密校验逻辑)
        User user = userService.getUserByUsername(loginDTO.getUsername());
        if (user == null || !user.getPassword().equals(loginDTO.getPassword())) {
            return Result.fail("用户名或密码错误");
        }
        // 生成JWT Token
        String token = jwtUtil.generateToken(user.getId());
        return Result.success(token);
    }
}

3. 商品服务(product-service)

(1)核心功能:商品缓存(Cache Aside Pattern)
java 复制代码
@Service
public class ProductServiceImpl implements ProductService {
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    private static final String CACHE_KEY_PRODUCT = "product:";

    // 查询商品详情(缓存优先)
    @Override
    public Product getProductById(Long productId) {
        // 1. 查缓存
        String key = CACHE_KEY_PRODUCT + productId;
        String json = redisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(json)) {
            return JSONUtil.toBean(json, Product.class);
        }
        // 2. 缓存未命中,查数据库
        Product product = productMapper.selectById(productId);
        if (product != null) {
            // 3. 存入缓存(过期时间1小时,加随机值防雪崩)
            redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(product), 
                    3600 + new Random().nextInt(600), TimeUnit.SECONDS);
        } else {
            // 4. 缓存空值(防穿透)
            redisTemplate.opsForValue().set(key, "", 60, TimeUnit.SECONDS);
        }
        return product;
    }

    // 更新商品(更新数据库+删除缓存)
    @Override
    @Transactional
    public boolean updateProduct(Product product) {
        // 1. 更新数据库
        boolean success = productMapper.updateById(product) > 0;
        if (success) {
            // 2. 删除缓存(避免缓存脏数据)
            String key = CACHE_KEY_PRODUCT + product.getId();
            redisTemplate.delete(key);
        }
        return success;
    }
}

4. 库存服务(inventory-service)

(1)核心功能:库存扣减(防超卖+分布式锁)
java 复制代码
@Service
public class InventoryServiceImpl implements InventoryService {
    @Autowired
    private InventoryMapper inventoryMapper;
    @Autowired
    private RedissonClient redissonClient;
    private static final String LOCK_KEY_INVENTORY = "lock:inventory:";

    // 预扣库存(TCC模式的Try方法)
    @Override
    public boolean deductStock(Long productId, Integer quantity, String orderId) {
        // 1. 分布式锁(Redisson),防止并发超卖
        RLock lock = redissonClient.getLock(LOCK_KEY_INVENTORY + productId);
        lock.lock(30, TimeUnit.SECONDS); // 锁超时30秒
        try {
            // 2. 查库存(带行锁,避免幻读)
            Inventory inventory = inventoryMapper.selectByProductIdForUpdate(productId);
            if (inventory == null || inventory.getStock() < quantity) {
                return false; // 库存不足
            }
            // 3. 预扣库存(锁定库存增加,可用库存减少)
            inventory.setStock(inventory.getStock() - quantity);
            inventory.setLockedStock(inventory.getLockedStock() + quantity);
            // 4. 记录库存日志(用于回滚)
            InventoryLog log = new InventoryLog();
            log.setProductId(productId);
            log.setQuantity(quantity);
            log.setOrderId(orderId);
            log.setOperateType(1); // 1=扣减
            inventoryLogMapper.insert(log);
            return inventoryMapper.updateById(inventory) > 0;
        } finally {
            lock.unlock(); // 释放锁
        }
    }

    // 确认扣减(TCC模式的Confirm方法)
    @Override
    public boolean confirmDeduct(Long productId, Integer quantity) {
        // 无需操作,TCC最终确认(实际可清理日志)
        return true;
    }

    // 回滚库存(TCC模式的Cancel方法)
    @Override
    public boolean cancelDeduct(Long productId, Integer quantity, String orderId) {
        RLock lock = redissonClient.getLock(LOCK_KEY_INVENTORY + productId);
        lock.lock(30, TimeUnit.SECONDS);
        try {
            // 回滚库存:可用库存增加,锁定库存减少
            Inventory inventory = inventoryMapper.selectById(productId);
            inventory.setStock(inventory.getStock() + quantity);
            inventory.setLockedStock(inventory.getLockedStock() - quantity);
            // 记录回滚日志
            InventoryLog log = new InventoryLog();
            log.setProductId(productId);
            log.setQuantity(quantity);
            log.setOrderId(orderId);
            log.setOperateType(2); // 2=回滚
            inventoryLogMapper.insert(log);
            return inventoryMapper.updateById(inventory) > 0;
        } finally {
            lock.unlock();
        }
    }
}

5. 订单服务(order-service)

(1)核心功能:创建订单(整合Seata TCC分布式事务)
java 复制代码
// 订单TCC接口(本地事务接口)
public interface OrderTccService {
    // Try:创建订单(待支付)+ 预扣库存
    @TwoPhaseBusinessAction(name = "createOrderTcc", commitMethod = "confirm", rollbackMethod = "cancel")
    boolean tryCreateOrder(@BusinessActionContextParameter(paramName = "orderDTO") OrderDTO orderDTO, BusinessActionContext context);

    // Confirm:确认订单(支付成功后)
    boolean confirm(BusinessActionContext context);

    // Cancel:取消订单(支付失败/超时)
    boolean cancel(BusinessActionContext context);
}

@Service
public class OrderTccServiceImpl implements OrderTccService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderItemMapper orderItemMapper;
    // Dubbo远程调用库存服务
    @DubboReference
    private InventoryService inventoryService;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Override
    @Transactional
    public boolean tryCreateOrder(OrderDTO orderDTO, BusinessActionContext context) {
        // 1. 生成订单号
        String orderSn = IdUtil.fastSimpleUUID();
        // 2. 创建主订单(状态:待支付)
        OrderMain orderMain = new OrderMain();
        orderMain.setOrderSn(orderSn);
        orderMain.setUserId(orderDTO.getUserId());
        orderMain.setTotalAmount(orderDTO.getTotalAmount());
        orderMain.setStatus(0); // 0=待支付
        orderMapper.insert(orderMain);
        // 3. 创建订单项
        for (OrderItemDTO itemDTO : orderDTO.getItems()) {
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderId(orderMain.getId());
            orderItem.setProductId(itemDTO.getProductId());
            orderItem.setProductName(itemDTO.getProductName());
            orderItem.setPrice(itemDTO.getPrice());
            orderItem.setQuantity(itemDTO.getQuantity());
            orderItemMapper.insert(orderItem);
        }
        // 4. 远程调用库存服务预扣库存(Dubbo)
        boolean deductSuccess = inventoryService.deductStock(
                orderDTO.getItems().get(0).getProductId(), // 简化:单商品订单,多商品需循环
                orderDTO.getItems().get(0).getQuantity(),
                orderSn
        );
        if (!deductSuccess) {
            throw new RuntimeException("库存不足");
        }
        // 5. 发送延迟队列(订单超时取消,30分钟延迟)
        rocketMQTemplate.send("order-delay-topic", 
                MessageBuilder.withPayload(orderSn)
                        .setHeader(RocketMQHeaders.DELAY_TIME_LEVEL, 9) // RocketMQ延迟级别9=30分钟
                        .build());
        // 6. 保存订单号到上下文(用于Confirm/Cancel)
        context.setActionContext("orderSn", orderSn);
        return true;
    }

    @Override
    @Transactional
    public boolean confirm(BusinessActionContext context) {
        // 支付成功,更新订单状态为"已支付"
        String orderSn = (String) context.getActionContext("orderSn");
        OrderMain orderMain = orderMapper.selectByOrderSn(orderSn);
        orderMain.setStatus(1); // 1=已支付
        orderMain.setPayTime(new Date());
        return orderMapper.updateById(orderMain) > 0;
    }

    @Override
    @Transactional
    public boolean cancel(BusinessActionContext context) {
        // 支付失败/超时,取消订单+回滚库存
        String orderSn = (String) context.getActionContext("orderSn");
        // 1. 更新订单状态为"已取消"
        OrderMain orderMain = orderMapper.selectByOrderSn(orderSn);
        orderMain.setStatus(-1); // -1=已取消
        orderMapper.updateById(orderMain);
        // 2. 远程调用库存服务回滚库存
        OrderItem orderItem = orderItemMapper.selectByOrderId(orderMain.getId()).get(0);
        inventoryService.cancelDeduct(orderItem.getProductId(), orderItem.getQuantity(), orderSn);
        return true;
    }
}
(2)订单超时取消(监听RocketMQ延迟队列)
java 复制代码
@Component
@RocketMQMessageListener(topic = "order-delay-topic", consumerGroup = "order-delay-consumer")
public class OrderDelayConsumer implements RocketMQListener<String> {
    @Autowired
    private OrderMainMapper orderMainMapper;
    @Autowired
    private OrderTccService orderTccService;

    @Override
    public void onMessage(String orderSn) {
        // 1. 查询订单状态
        OrderMain orderMain = orderMainMapper.selectByOrderSn(orderSn);
        if (orderMain == null) {
            return;
        }
        // 2. 若订单仍为"待支付",触发取消逻辑
        if (orderMain.getStatus() == 0) {
            // 模拟Seata Cancel操作(实际通过Seata事务上下文触发)
            BusinessActionContext context = new BusinessActionContext();
            context.setActionContext("orderSn", orderSn);
            orderTccService.cancel(context);
            System.out.println("订单" + orderSn + "超时未支付,已自动取消");
        }
    }
}

6. 支付服务(pay-service)

(1)核心功能:对接支付宝支付
java 复制代码
@Service
public class AlipayServiceImpl implements PayService {
    @Autowired
    private PaymentMapper paymentMapper;
    @DubboReference
    private OrderTccService orderTccService;
    @Value("${alipay.appId}")
    private String appId;
    @Value("${alipay.privateKey}")
    private String privateKey;
    @Value("${alipay.publicKey}")
    private String publicKey;
    @Value("${alipay.notifyUrl}")
    private String notifyUrl;

    // 发起支付宝支付
    @Override
    public String createAlipayOrder(String orderSn, Long userId, BigDecimal amount) {
        try {
            // 1. 构建支付宝客户端
            AlipayClient client = new DefaultAlipayClient(
                    "https://openapi.alipay.com/gateway.do",
                    appId, privateKey, "json", "UTF-8", publicKey, "RSA2");
            // 2. 构建支付请求
            AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            request.setReturnUrl("http://localhost:8080/pay/return"); // 同步回调地址
            request.setNotifyUrl(notifyUrl); // 异步回调地址
            // 3. 封装请求参数
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderSn); // 订单号(与订单服务一致)
            bizContent.put("total_amount", amount); // 支付金额
            bizContent.put("subject", "电商订单支付"); // 订单标题
            bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); // 支付产品码
            request.setBizContent(bizContent.toString());
            // 4. 发起请求,获取支付链接
            AlipayTradePagePayResponse response = client.pageExecute(request);
            if (response.isSuccess()) {
                // 5. 记录支付记录
                Payment payment = new Payment();
                payment.setOrderSn(orderSn);
                payment.setUserId(userId);
                payment.setPayAmount(amount);
                payment.setPayType(1); // 1=支付宝
                payment.setStatus(0); // 0=待支付
                paymentMapper.insert(payment);
                return response.getBody(); // 返回支付宝支付页面HTML
            } else {
                throw new RuntimeException("支付宝支付创建失败:" + response.getMsg());
            }
        } catch (Exception e) {
            throw new RuntimeException("支付异常", e);
        }
    }

    // 支付宝异步回调(验证支付结果)
    @Override
    public String handleAlipayNotify(Map<String, String> params) {
        try {
            // 1. 验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(
                    params, publicKey, "UTF-8", "RSA2");
            if (!signVerified) {
                return "fail"; // 签名验证失败
            }
            // 2. 验证支付状态
            String tradeStatus = params.get("trade_status");
            if (!"TRADE_SUCCESS".equals(tradeStatus)) {
                return "fail";
            }
            // 3. 获取订单信息
            String orderSn = params.get("out_trade_no");
            String tradeNo = params.get("trade_no"); // 支付宝交易号
            BigDecimal payAmount = new BigDecimal(params.get("total_amount"));
            // 4. 更新支付记录状态
            Payment payment = paymentMapper.selectByOrderSn(orderSn);
            if (payment == null || payment.getStatus() != 0) {
                return "fail"; // 支付记录不存在或已支付
            }
            payment.setStatus(1); // 1=已支付
            payment.setPayTime(new Date());
            payment.setTradeNo(tradeNo);
            paymentMapper.updateById(payment);
            // 5. 触发订单TCC Confirm操作(确认订单)
            BusinessActionContext context = new BusinessActionContext();
            context.setActionContext("orderSn", orderSn);
            orderTccService.confirm(context);
            return "success"; // 回调成功
        } catch (Exception e) {
            return "fail";
        }
    }
}

六、服务注册与配置中心(Nacos实战)

1. Nacos部署(Docker方式)

bash 复制代码
# 拉取Nacos镜像
docker pull nacos/nacos-server:v2.2.3
# 启动Nacos(单机模式)
docker run -d \
  -p 8848:8848 \
  -e MODE=standalone \
  -e SPRING_DATASOURCE_PLATFORM=mysql \
  -e MYSQL_SERVICE_HOST=192.168.1.100 \
  -e MYSQL_SERVICE_PORT=3306 \
  -e MYSQL_SERVICE_DB_NAME=nacos_config \
  -e MYSQL_SERVICE_USER=root \
  -e MYSQL_SERVICE_PASSWORD=123456 \
  --name nacos \
  nacos/nacos-server:v2.2.3

2. Nacos配置中心使用

(1)创建配置文件
  • 命名规则:${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
  • 示例:order-service-dev.yaml(订单服务dev环境配置)
  • 配置内容:
yaml 复制代码
# 数据库配置
spring:
  datasource:
    url: jdbc:mysql://192.168.1.100:3306/order_db?useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
# Redis配置
redis:
  host: 192.168.1.100
  port: 6379
  password: 123456
  database: 0
# JWT配置
jwt:
  secret: abc1234567890xyz
  expire: 86400 # 24小时
# Sentinel配置
sentinel:
  transport:
    dashboard: 192.168.1.100:8080 # Sentinel控制台地址
(2)动态刷新配置

在需要动态刷新的类上添加@RefreshScope注解:

java 复制代码
@RestController
@RequestMapping("/order")
@RefreshScope // 动态刷新配置
public class OrderController {
    @Value("${order.timeout:30}") // 默认30分钟
    private Integer orderTimeout;

    // 接口使用orderTimeout配置
}

七、网关层设计(Sentinel Gateway)

1. 网关配置(application.yml)

yaml 复制代码
spring:
  cloud:
    sentinel:
      gateway:
        routes:
          - id: user-service
            uri: lb://user-service
            predicates:
              - Path=/api/user/**filters:
              - StripPrefix=1 # 去掉/api前缀
              - name: SentinelGatewayFilter # Sentinel限流过滤器
          - id: order-service
            uri: lb://order-service
            predicates:
              - Path=/api/order/**
            filters:
              - StripPrefix=1
              - name: SentinelGatewayFilter
        rules:
          # 限流规则:按路径限流(订单服务最大QPS=1000)
          - resource: order-service
            limitApp: default
            grade: 1 # 1=QPS限流
            count: 1000 # 最大QPS
            intervalSec: 1 # 统计时间窗口(秒)
          # 限流规则:按用户限流(单用户最大QPS=100)
          - resource: user-service
            limitApp: ${spring.application.name}
            grade: 1
            count: 100
            intervalSec: 1
            controlBehavior: 2 # 2=匀速排队

2. 统一鉴权过滤器

java 复制代码
@Component
public class AuthFilter implements GlobalFilter, Ordered {
    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1. 跳过登录接口
        String path = exchange.getRequest().getPath().value();
        if (path.contains("/user/login")) {
            return chain.filter(exchange);
        }
        // 2. 获取Token
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (StrUtil.isBlank(token) || !token.startsWith("Bearer ")) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        token = token.substring(7);
        // 3. 验证Token
        if (!jwtUtil.validateToken(token)) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        // 4. 解析用户ID,存入请求头
        Long userId = jwtUtil.getUserIdFromToken(token);
        exchange.getRequest().mutate().header("userId", userId.toString()).build();
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1; // 执行顺序:先鉴权,再限流
    }
}

八、分布式事务一致性保障(Seata实战)

1. Seata部署(Docker)

bash 复制代码
# 拉取Seata镜像
docker pull seataio/seata-server:1.6.1
# 启动Seata Server(TC事务协调器)
docker run -d \
  -p 8091:8091 \
  -e SEATA_IP=192.168.1.100 \
  -e SEATA_PORT=8091 \
  -e SEATA_CONFIG_NAME=file:/root/seata-config/registry \
  -v /root/seata-config:/root/seata-config \
  --name seata-server \
  seataio/seata-server:1.6.1

2. Seata配置(registry.conf)

yaml 复制代码
registry {
  type = "nacos" # 注册中心用Nacos
  nacos {
    application = "seata-server"
    serverAddr = "192.168.1.100:8848"
    group = "SEATA_GROUP"
    namespace = "dev"
    username = "nacos"
    password = "nacos"
  }
}

config {
  type = "nacos" # 配置中心用Nacos
  nacos {
    serverAddr = "192.168.1.100:8848"
    namespace = "dev"
    group = "SEATA_GROUP"
    username = "nacos"
    password = "nacos"
  }
}

3. 微服务集成Seata(application.yml)

yaml 复制代码
seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: order_tx_group # 事务组(需与Seata配置一致)
  registry:
    type: nacos
    nacos:
      server-addr: 192.168.1.100:8848
      group: SEATA_GROUP
      namespace: dev
  config:
    type: nacos
    nacos:
      server-addr: 192.168.1.100:8848
      group: SEATA_GROUP
      namespace: dev
  service:
    vgroup-mapping:
      order_tx_group: default # 事务组映射
  client:
    rm:
      report-success-enable: true

4. 分布式事务模式选择

模式 适用场景 优点 缺点
TCC 核心链路(下单-库存-支付) 强一致性、性能高 代码侵入性高(需写Try/Confirm/Cancel)
AT 非核心链路(如订单通知) 无侵入性、易实现 弱一致性(最终一致)、依赖数据库事务
可靠消息最终一致 异步场景(如日志同步) 解耦、性能高 一致性延迟、需处理消息丢失

九、缓存问题全解(Redis+一致性+防雪崩)

1. 缓存三大问题解决方案

问题 解决方案 代码示例
缓存穿透(查不存在的数据) 布隆过滤器+缓存空值 java // 布隆过滤器初始化 @PostConstruct public void initBloomFilter() { List<Long> productIds = productMapper.selectAllProductIds(); BloomFilter<Long> filter = BloomFilter.create(Funnels.longFunnel(), productIds.size(), 0.001); for (Long id : productIds) { filter.put(id); } redisTemplate.opsForValue().set("bloom:product", filter); } // 查询前校验布隆过滤器 if (!filter.mightContain(productId)) { return null; }
缓存击穿(热点key过期) 互斥锁+热点数据永不过期 java // 互斥锁解决击穿 String lockKey = "lock:product:" + productId; RLock lock = redissonClient.getLock(lockKey); lock.tryLock(5, 30, TimeUnit.SECONDS); try { // 再次查缓存,避免重复查库 String json = redisTemplate.opsForValue().get(cacheKey); if (StrUtil.isNotBlank(json)) { return JSONUtil.toBean(json, Product.class); } // 查库并更新缓存... } finally { lock.unlock(); }
缓存雪崩(大量key同时过期) 过期时间加随机值+Redis集群 java // 过期时间加随机值 int expire = 3600 + new Random().nextInt(600); redisTemplate.opsForValue().set(cacheKey, json, expire, TimeUnit.SECONDS);

2. 缓存一致性(Cache Aside Pattern)

  • 读流程:先查缓存→缓存命中直接返回→缓存未命中查数据库→存入缓存
  • 写流程:先更数据库→再删缓存(而非更新缓存)
  • 为什么删缓存而非更新?避免并发场景下的缓存脏数据(如A更新数据库,B更新缓存,A再删缓存,导致缓存是B的旧数据)

十、中间件选型与使用

1. RocketMQ(消息队列)

(1)核心使用场景
  • 异步通信:订单创建后通知用户、日志收集
  • 延迟队列:订单超时取消(30分钟延迟)
  • 削峰填谷:秒杀场景异步下单(避免直接压垮数据库)
(2)关键配置
yaml 复制代码
rocketmq:
  name-server: 192.168.1.100:9876
  producer:
    group: order-producer-group
    send-message-timeout: 3000
  consumer:
    group: order-consumer-group
    message-model: CLUSTERING # 集群消费模式

2. Redis(缓存+分布式锁)

(1)Redis集群部署(3主3从)
bash 复制代码
# 用Docker Compose部署Redis集群(省略复杂配置,实际生产用官方集群工具)
version: '3'
services:
  redis-master1:
    image: redis:6.2.6
    command: redis-server --port 6379 --requirepass 123456
    ports:
      - "6379:6379"
  redis-slave1:
    image: redis:6.2.6
    command: redis-server --port 6380 --requirepass 123456 --slaveof redis-master1 6379
    ports:
      - "6380:6380"
# 其他主从节点配置省略...
(2)Redisson分布式锁
java 复制代码
@Autowired
private RedissonClient redissonClient;

// 秒杀场景分布式锁
public boolean seckill(Long productId, Long userId) {
    String lockKey = "lock:seckill:" + productId;
    RLock lock = redissonClient.getLock(lockKey);
    try {
        // 尝试加锁,5秒超时,30秒自动释放
        boolean locked = lock.tryLock(5, 30, TimeUnit.SECONDS);
        if (!locked) {
            return false; // 秒杀失败(已被抢完)
        }
        // 查库存→扣库存→创建订单(省略业务逻辑)
        return true;
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

3. Sharding-JDBC(分库分表)

(1)订单表分库分表配置(application.yml)
yaml 复制代码
spring:
  shardingsphere:
    datasource:
      names: db0,db1 # 两个分库
      db0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.1.100:3306/order_db0?useSSL=false
        username: root
        password: 123456
      db1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.1.100:3306/order_db1?useSSL=false
        username: root
        password: 123456
    rules:
      sharding:
        tables:
          order_main: # 订单主表
            actual-data-nodes: db${0..1}.order_main_${202401..202412} # 分库分表规则(2库12表)
            database-strategy: # 分库策略:按用户ID哈希
              standard:
                sharding-column: user_id
                sharding-algorithm-name: order_db_inline
            table-strategy: # 分表策略:按创建时间分月
              standard:
                sharding-column: create_time
                sharding-algorithm-name: order_table_inline
        sharding-algorithms:
          order_db_inline:
            type: INLINE
            props:
              algorithm-expression: db${user_id % 2} # 用户ID%2→db0/db1
          order_table_inline:
            type: INLINE
            props:
              algorithm-expression: order_main_${date_format(create_time,'yyyyMM')} # 按年月分表

十一、核心业务实现

1. 订单超时自动取消(两种方案对比)

方案 实现方式 优点 缺点 推荐场景
RocketMQ延迟队列 订单创建时发送延迟消息,到期消费 可靠性高、延迟精度高 依赖RocketMQ集群 核心订单场景(推荐)
Redis过期键通知 订单创建时设置Redis键,监听过期事件 实现简单、无中间件依赖 过期通知可能延迟、不保证100%送达 非核心场景(如优惠券过期)

2. 库存防超卖(三重保障)

  1. 数据库层面:库存扣减时加行锁(select ... for update
  2. 缓存层面:Redis分布式锁(Redisson)
  3. 业务层面:库存预扣+TCC回滚(支付失败回滚库存)

十二、特殊场景优化(秒杀/高并发)

1. 秒杀场景优化方案

(1)架构层面
  • 限流:网关限流(Sentinel)+ 服务限流(Sentinel注解)
  • 削峰:RocketMQ异步下单(避免直接操作数据库)
  • 缓存预热:秒杀商品库存提前加载到Redis(避免缓存穿透)
  • 隔离:秒杀服务独立部署(避免影响核心服务)
(2)代码层面
java 复制代码
@Service
public class SeckillServiceImpl implements SeckillService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    private static final String SECKILL_STOCK_KEY = "seckill:stock:";
    private static final String SECKILL_USER_KEY = "seckill:user:"; // 防重复秒杀

    @Override
    @SentinelResource(value = "seckill", blockHandler = "seckillBlockHandler")
    public Result<String> seckill(Long productId, Long userId) {
        String stockKey = SECKILL_STOCK_KEY + productId;
        String userKey = SECKILL_USER_KEY + productId + ":" + userId;

        // 1. 防重复秒杀(Redis判断用户是否已秒杀)
        Boolean hasSeckill = redisTemplate.hasKey(userKey);
        if (Boolean.TRUE.equals(hasSeckill)) {
            return Result.fail("您已参与秒杀,请勿重复提交");
        }

        // 2. 扣减Redis库存(原子操作,避免超卖)
        Long stock = redisTemplate.opsForValue().decrement(stockKey);
        if (stock == null || stock < 0) {
            // 库存不足,回滚Redis库存
            redisTemplate.opsForValue().increment(stockKey);
            return Result.fail("秒杀已结束,库存不足");
        }

        // 3. 记录秒杀用户(防止重复)
        redisTemplate.opsForValue().set(userKey, "1", 24, TimeUnit.HOURS);

        // 4. 发送异步消息,创建订单(削峰填谷)
        SeckillDTO seckillDTO = new SeckillDTO();
        seckillDTO.setProductId(productId);
        seckillDTO.setUserId(userId);
        rocketMQTemplate.send("seckill-order-topic", 
                MessageBuilder.withPayload(seckillDTO).build());

        return Result.success("秒杀成功,订单正在创建");
    }

    // Sentinel限流降级回调
    public Result<String> seckillBlockHandler(Long productId, Long userId, BlockException e) {
        return Result.fail("当前秒杀人数过多,请稍后再试");
    }
}

2. 接口幂等性(防重复提交)

  • 实现方式:基于订单号/请求ID去重(Redis)
  • 代码示例:
java 复制代码
// 幂等性注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
    long expire() default 300; // 过期时间(秒)
}

// 幂等性切面
@Component
@Aspect
public class IdempotentAspect {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Around("@annotation(idempotent)")
    public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {
        // 1. 获取请求ID(从请求头或参数中)
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String requestId = request.getHeader("Request-Id");
        if (StrUtil.isBlank(requestId)) {
            return Result.fail("Request-Id不能为空");
        }
        // 2. 构建Redis键
        String key = "idempotent:" + requestId;
        // 3. 尝试设置Redis键(原子操作)
        Boolean success = redisTemplate.opsForValue().setIfAbsent(key, "1", idempotent.expire(), TimeUnit.SECONDS);
        if (Boolean.FALSE.equals(success)) {
            return Result.fail("请勿重复提交");
        }
        // 4. 执行目标方法
        return joinPoint.proceed();
    }
}

// 接口使用
@PostMapping("/createOrder")
@Idempotent(expire = 60) // 60秒内防重复提交
public Result<String> createOrder(@RequestBody OrderDTO orderDTO) {
    // 下单逻辑
}

十三、环境配置与部署(Docker+K8s)

1. Docker镜像构建(Dockerfile)

dockerfile 复制代码
# 基础镜像
FROM openjdk:11-jre-slim
# 工作目录
WORKDIR /app
# 复制jar包
COPY target/order-service-1.0.0.jar app.jar
# 暴露端口
EXPOSE 8080
# 启动命令
ENTRYPOINT ["java", "-jar", "app.jar", "--spring.profiles.active=prod"]

2. K8s部署配置(order-service-deployment.yaml)

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  namespace: e-commerce
spec:
  replicas: 3 # 3个副本(高可用)
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
      - name: order-service
        image: order-service:1.0.0
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "0.5"
            memory: "512Mi"
        livenessProbe: # 健康检查
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: order-service
  namespace: e-commerce
spec:
  selector:
    app: order-service
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP # 集群内部访问

3. 部署流程

bash 复制代码
# 1. 打包Jar包
mvn clean package -Dmaven.test.skip=true
# 2. 构建Docker镜像
docker build -t order-service:1.0.0 .
# 3. 推送镜像到仓库(Harbor)
docker tag order-service:1.0.0 192.168.1.100:8080/e-commerce/order-service:1.0.0
docker push 192.168.1.100:8080/e-commerce/order-service:1.0.0
# 4. K8s部署
kubectl apply -f order-service-deployment.yaml
# 5. 查看部署状态
kubectl get pods -n e-commerce

十四、监控与运维

1. 全链路监控(Prometheus+Grafana)

(1)微服务集成Prometheus
xml 复制代码
<!-- pom.xml依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
(2)application.yml配置
yaml 复制代码
management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus # 暴露监控端点
  metrics:
    tags:
      application: ${spring.application.name} # 增加应用名称标签
  endpoint:
    health:
      show-details: always # 显示健康详情
(3)Grafana面板配置
  • 导入Spring Boot监控模板(ID:12856)
  • 监控指标:QPS、响应时间、错误率、JVM内存、数据库连接数

2. 日志收集(ELK)

  • Logstash:收集微服务日志(Docker日志挂载到宿主机)
  • Elasticsearch:存储日志数据
  • Kibana:可视化日志查询与分析

3. 告警机制

  • 配置Prometheus告警规则(如响应时间>3s、错误率>5%)
  • 集成AlertManager,通过邮件/钉钉/短信发送告警

总结

本文基于Spring Cloud Alibaba生态,详细讲解了微服务电商项目的全流程开发,从需求分析、数据库设计、技术选型,到核心服务开发、分布式事务、缓存优化、高并发处理,再到部署运维,覆盖了电商项目的核心痛点(如分布式一致性、库存超卖、订单超时、高并发秒杀)。

关键亮点:

  1. 基于Seata TCC模式保证核心链路一致性
  2. 多重缓存策略解决缓存穿透/击穿/雪崩问题
  3. RocketMQ延迟队列实现订单超时取消
  4. 分布式锁+数据库行锁双重防超卖
  5. K8s容器化部署保证高可用

实际开发中,需根据业务规模调整架构(如小体量项目可简化分库分表、Seata用AT模式),同时定期进行故障演练(如Chaos Monkey模拟服务宕机),验证熔断、降级、兜底逻辑的有效性。

相关推荐
7***53342 小时前
微服务分布式事务解决方案
分布式·微服务·架构
S***q1922 小时前
后端服务架构设计:从单体到微服务
java·微服务·架构
T***u3332 小时前
微服务书籍
java·微服务·架构
pengzhuofan2 小时前
微服务初识:核心概念与SpringCloud生态
spring cloud·微服务·架构
赋能大师兄2 小时前
4G到5G核心网架构演进介绍
5g·架构
Vanranrr3 小时前
车机项目中的 Widget 设计反思:从“能用”到“好用”的改进方向
c语言·c++·架构
纯爱掌门人3 小时前
别再死磕框架了!你的技术路线图该更新了
前端·架构·前端框架
kfyty7254 小时前
loveqq 作为网关框架时如何修改请求体 / 响应体,和 spring 又有什么区别?
后端·架构
Z***25804 小时前
后端服务网格实践,Istio与Linkerd对比
云原生·istio