从零到一:外卖系统架构设计与实战

一、外卖系统业务场景分析

1.1 核心业务流程

外卖系统是一个典型的O2O(Online to Offline)电商平台,涉及用户、商家、骑手三方角色,核心业务流程包括:

用户下单流程

  1. 用户浏览商家列表和菜品
  2. 选择菜品加入购物车
  3. 选择收货地址和支付方式
  4. 提交订单并支付
  5. 等待商家接单和骑手配送
  6. 确认收货并评价

商家接单流程

  1. 接收新订单通知
  2. 确认接单并开始制作
  3. 制作完成通知骑手取餐
  4. 订单完成或取消

骑手配送流程

  1. 抢单或系统派单
  2. 到店取餐
  3. 配送至用户
  4. 完成配送

1.2 业务特点与挑战

高并发:用餐高峰期订单量激增,需要支持大量用户同时下单

实时性:订单状态、骑手位置需要实时更新

数据一致性:库存扣减、订单状态流转需要保证数据一致性

高可用:7×24小时服务,不能出现长时间宕机

可扩展性:支持业务快速扩张,能够水平扩展

二、系统架构设计原则

2.1 设计原则

微服务化:将系统拆分为多个独立的微服务,每个服务负责单一职责

高可用:多节点部署,避免单点故障

可扩展:支持水平扩展,应对业务增长

松耦合:服务间通过API或消息队列通信,降低依赖

容错性:具备熔断、降级、限流等容错机制

2.2 技术选型

后端框架:Spring Boot + Spring Cloud

数据库:MySQL(主从复制)+ Redis(缓存)+ MongoDB(日志)

消息队列:RabbitMQ/Kafka

服务注册与发现:Nacos/Eureka

配置中心:Nacos Config

服务网关:Spring Cloud Gateway

分布式事务:Seata

监控告警:Prometheus + Grafana + ELK

容器化:Docker + Kubernetes

三、系统架构分层设计

3.1 整体架构图

┌─────────────────────────────────────────────────┐
│                   客户端层                       │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐         │
│  │  Web端  │  │  App端  │  │  小程序  │         │
│  └─────────┘  └─────────┘  └─────────┘         │
└─────────────────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────┐
│                   网关层                        │
│  ┌─────────────────────────────────────────────┐ │
│  │              API Gateway                    │ │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐    │ │
│  │  │  认证   │  │  限流   │  │  日志   │    │ │
│  │  └─────────┘  └─────────┘  └─────────┘    │ │
│  └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────┐
│                   业务服务层                     │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐         │
│  │ 用户服务 │  │ 商家服务 │  │ 订单服务 │         │
│  └─────────┘  └─────────┘  └─────────┘         │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐         │
│  │ 菜品服务 │  │ 支付服务 │  │ 配送服务 │         │
│  └─────────┘  └─────────┘  └─────────┘         │
└─────────────────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────┐
│                   数据层                        │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐         │
│  │ MySQL   │  │ Redis   │  │ MongoDB │         │
│  └─────────┘  └─────────┘  └─────────┘         │
└─────────────────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────┐
│                   基础设施层                    │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐         │
│  │  监控   │  │  日志   │  │  配置   │         │
│  └─────────┘  └─────────┘  └─────────┘         │
└─────────────────────────────────────────────────┘

3.2 微服务拆分

用户服务(user-service)

  • 用户注册、登录、个人信息管理
  • 收货地址管理
  • 用户积分、优惠券管理

商家服务(merchant-service)

  • 商家入驻、信息管理
  • 店铺管理、营业状态
  • 商家统计报表

菜品服务(dish-service)

  • 菜品分类管理
  • 菜品信息、库存管理
  • 菜品评价管理

订单服务(order-service)

  • 订单创建、查询、取消
  • 订单状态管理
  • 订单统计

支付服务(payment-service)

  • 支付渠道对接(微信、支付宝)
  • 支付回调处理
  • 退款处理

配送服务(delivery-service)

  • 骑手管理、位置追踪
  • 订单分配、抢单
  • 配送状态管理

消息服务(message-service)

  • 短信、推送通知
  • 站内信、系统公告

四、核心业务流程设计

4.1 用户下单流程

时序图

用户 -> 网关: 提交订单
网关 -> 订单服务: 创建订单
订单服务 -> 菜品服务: 校验库存
菜品服务 -> 订单服务: 库存充足
订单服务 -> 支付服务: 生成支付订单
支付服务 -> 订单服务: 支付订单生成成功
订单服务 -> 用户: 返回支付信息
用户 -> 支付服务: 发起支付
支付服务 -> 订单服务: 支付成功回调
订单服务 -> 菜品服务: 扣减库存
菜品服务 -> 订单服务: 库存扣减成功
订单服务 -> 商家服务: 通知新订单
订单服务 -> 消息服务: 发送订单创建通知

关键代码实现

// 订单服务 - 创建订单
@Transactional
public OrderDTO createOrder(CreateOrderRequest request) {
    // 1. 校验用户信息
    UserDTO user = userService.getUserById(request.getUserId());
    if (user == null) {
        throw new BusinessException("用户不存在");
    }
    
    // 2. 校验菜品库存
    List<DishStockDTO> stockList = dishService.checkStock(request.getItems());
    if (stockList.stream().anyMatch(item -> !item.isSufficient())) {
        throw new BusinessException("库存不足");
    }
    
    // 3. 生成订单
    Order order = buildOrder(request, user);
    orderMapper.insert(order);
    
    // 4. 生成支付订单
    PaymentOrderDTO paymentOrder = paymentService.createPaymentOrder(order);
    
    // 5. 扣减库存(异步消息)
    dishService.reduceStockAsync(request.getItems());
    
    // 6. 发送通知
    messageService.sendOrderCreatedMessage(order);
    
    return convertToDTO(order, paymentOrder);
}

4.2 支付回调处理

// 支付服务 - 支付回调
@PostMapping("/callback/{channel}")
public String paymentCallback(@PathVariable String channel, 
                            @RequestBody String body) {
    // 1. 验证签名
    if (!verifySignature(channel, body)) {
        return "fail";
    }
    
    // 2. 解析回调参数
    PaymentCallbackParam param = parseCallbackParam(channel, body);
    
    // 3. 处理支付结果
    if (param.isSuccess()) {
        // 支付成功
        paymentSuccess(param.getOrderNo(), param.getTradeNo());
    } else {
        // 支付失败
        paymentFail(param.getOrderNo(), param.getFailReason());
    }
    
    return "success";
}

private void paymentSuccess(String orderNo, String tradeNo) {
    // 1. 更新支付订单状态
    paymentOrderService.updateStatus(orderNo, PaymentStatus.SUCCESS, tradeNo);
    
    // 2. 通知订单服务
    orderService.paymentSuccess(orderNo);
    
    // 3. 发送支付成功通知
    messageService.sendPaymentSuccessMessage(orderNo);
}

五、数据库设计

5.1 核心表结构

用户表(user)

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(100) NOT NULL COMMENT '密码',
  `phone` varchar(20) NOT NULL COMMENT '手机号',
  `avatar` varchar(200) DEFAULT NULL COMMENT '头像',
  `status` tinyint(1) 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`),
  UNIQUE KEY `uk_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

商家表(merchant)

CREATE TABLE `merchant` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '商家ID',
  `name` varchar(100) NOT NULL COMMENT '商家名称',
  `logo` varchar(200) DEFAULT NULL COMMENT '商家logo',
  `address` varchar(200) NOT NULL COMMENT '商家地址',
  `phone` varchar(20) NOT NULL COMMENT '联系电话',
  `business_hours` varchar(100) DEFAULT NULL COMMENT '营业时间',
  `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '状态:0-未审核,1-审核通过,2-审核拒绝,3-营业中,4-已关闭',
  `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 COMMENT='商家表';

菜品表(dish)

CREATE TABLE `dish` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '菜品ID',
  `merchant_id` bigint(20) NOT NULL COMMENT '商家ID',
  `name` varchar(100) NOT NULL COMMENT '菜品名称',
  `description` varchar(500) DEFAULT NULL COMMENT '菜品描述',
  `price` decimal(10,2) NOT NULL COMMENT '价格',
  `stock` int(11) NOT NULL DEFAULT '0' COMMENT '库存',
  `status` tinyint(1) 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`),
  KEY `idx_merchant_id` (`merchant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='菜品表';

订单表(order)

CREATE TABLE `order` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  `order_no` varchar(32) NOT NULL COMMENT '订单编号',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `merchant_id` bigint(20) NOT NULL COMMENT '商家ID',
  `total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额',
  `pay_amount` decimal(10,2) NOT NULL COMMENT '实际支付金额',
  `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '订单状态:0-待支付,1-已支付,2-商家接单,3-配送中,4-已完成,5-已取消',
  `delivery_address` varchar(200) NOT NULL COMMENT '配送地址',
  `remark` varchar(200) DEFAULT 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`),
  UNIQUE KEY `uk_order_no` (`order_no`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_merchant_id` (`merchant_id`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

订单明细表(order_item)

CREATE TABLE `order_item` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单明细ID',
  `order_id` bigint(20) NOT NULL COMMENT '订单ID',
  `dish_id` bigint(20) NOT NULL COMMENT '菜品ID',
  `dish_name` varchar(100) NOT NULL COMMENT '菜品名称',
  `price` decimal(10,2) NOT NULL COMMENT '单价',
  `quantity` int(11) NOT NULL COMMENT '数量',
  `amount` decimal(10,2) NOT NULL COMMENT '总价',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `idx_order_id` (`order_id`),
  KEY `idx_dish_id` (`dish_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';

5.2 分库分表策略

垂直分库

  • 用户库:user、user_address等表
  • 订单库:order、order_item等表
  • 商品库:merchant、dish等表

水平分表

  • 订单表按用户ID分表:order_0 ~ order_31
  • 订单明细表按订单ID分表:order_item_0 ~ order_item_31
  • 使用ShardingSphere实现分库分表

六、缓存设计

6.1 缓存策略

多级缓存架构

  • 本地缓存(Caffeine):热点数据,毫秒级响应
  • 分布式缓存(Redis):共享数据,秒级响应
  • 数据库:持久化存储

缓存穿透:使用布隆过滤器或缓存空值

缓存击穿:使用互斥锁或永不过期

缓存雪崩:设置不同的过期时间

6.2 缓存应用场景

用户信息缓存

@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String USER_CACHE_KEY = "user:";
    private static final long USER_CACHE_TTL = 3600; // 1小时
    
    @Cacheable(value = "user", key = "#id")
    public UserDTO getUserById(Long id) {
        return userMapper.selectById(id);
    }
    
    public UserDTO getUserByIdWithRedis(Long id) {
        String key = USER_CACHE_KEY + id;
        UserDTO user = (UserDTO) redisTemplate.opsForValue().get(key);
        if (user != null) {
            return user;
        }
        
        user = userMapper.selectById(id);
        if (user != null) {
            redisTemplate.opsForValue().set(key, user, USER_CACHE_TTL, TimeUnit.SECONDS);
        }
        return user;
    }
}

菜品信息缓存

@Service
public class DishServiceImpl implements DishService {
    
    @Autowired
    private DishMapper dishMapper;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String DISH_CACHE_KEY = "dish:";
    private static final long DISH_CACHE_TTL = 1800; // 30分钟
    
    public DishDTO getDishById(Long id) {
        String key = DISH_CACHE_KEY + id;
        DishDTO dish = (DishDTO) redisTemplate.opsForValue().get(key);
        if (dish != null) {
            return dish;
        }
        
        dish = dishMapper.selectById(id);
        if (dish != null) {
            redisTemplate.opsForValue().set(key, dish, DISH_CACHE_TTL, TimeUnit.SECONDS);
        }
        return dish;
    }
    
    @CacheEvict(value = "dish", key = "#id")
    public void updateDish(DishDTO dish) {
        dishMapper.updateById(dish);
        // 清除缓存
        String key = DISH_CACHE_KEY + dish.getId();
        redisTemplate.delete(key);
    }
}

七、消息队列应用

7.1 消息队列选型

RabbitMQ:适合业务消息,支持多种消息模式

Kafka:适合日志、大数据处理,高吞吐量

7.2 消息应用场景

订单创建消息

@Component
public class OrderCreatedProducer {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public void sendOrderCreatedMessage(OrderDTO order) {
        rabbitTemplate.convertAndSend(
            "order.exchange",
            "order.created",
            order
        );
    }
}

@Component
public class OrderCreatedConsumer {
    
    @RabbitListener(queues = "order.created.queue")
    public void handleOrderCreated(OrderDTO order) {
        // 1. 发送短信通知
        messageService.sendSms(order.getUserPhone(), "订单创建成功");
        
        // 2. 发送推送通知
        pushService.pushToUser(order.getUserId(), "您有新的订单");
        
        // 3. 通知商家
        merchantService.notifyNewOrder(order.getMerchantId(), order);
    }
}

库存扣减消息

@Component
public class StockReduceProducer {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public void sendStockReduceMessage(List<OrderItemDTO> items) {
        rabbitTemplate.convertAndSend(
            "stock.exchange",
            "stock.reduce",
            items
        );
    }
}

@Component
public class StockReduceConsumer {
    
    @RabbitListener(queues = "stock.reduce.queue")
    public void handleStockReduce(List<OrderItemDTO> items) {
        for (OrderItemDTO item : items) {
            dishService.reduceStock(item.getDishId(), item.getQuantity());
        }
    }
}

八、分布式事务处理

8.1 事务场景

下单事务

  • 创建订单(订单服务)
  • 扣减库存(菜品服务)
  • 生成支付订单(支付服务)

支付回调事务

  • 更新支付状态(支付服务)
  • 更新订单状态(订单服务)
  • 发送通知(消息服务)

8.2 解决方案

本地消息表

@Service
public class OrderServiceImpl implements OrderService {
    
    @Transactional
    public void createOrder(CreateOrderRequest request) {
        // 1. 创建订单
        Order order = buildOrder(request);
        orderMapper.insert(order);
        
        // 2. 保存本地消息
        LocalMessage message = new LocalMessage();
        message.setBizId(order.getId());
        message.setTopic("order.created");
        message.setContent(JSON.toJSONString(order));
        message.setStatus(0);
        localMessageMapper.insert(message);
        
        // 3. 发送消息(定时任务扫描发送)
    }
}

Seata AT模式

@GlobalTransactional
public void createOrder(CreateOrderRequest request) {
    // 1. 创建订单
    orderService.createOrder(request);
    
    // 2. 扣减库存
    dishService.reduceStock(request.getItems());
    
    // 3. 生成支付订单
    paymentService.createPaymentOrder(request.getOrderNo());
}

九、高可用设计

9.1 服务高可用

多节点部署:每个服务至少部署2个节点

负载均衡:使用Nginx或Spring Cloud Gateway实现负载均衡

服务熔断:使用Hystrix或Sentinel实现熔断降级

服务限流:使用Sentinel实现接口限流

9.2 数据库高可用

主从复制:MySQL主从复制,读写分离

分库分表:使用ShardingSphere实现分库分表

数据库监控:监控慢查询、连接数、锁等待

9.3 缓存高可用

Redis集群:Redis Cluster或Sentinel模式

缓存预热:定时任务预热热点数据

缓存降级:缓存不可用时降级到数据库

十、监控与告警

10.1 监控体系

应用监控

  • JVM监控:堆内存、GC、线程数
  • 接口监控:响应时间、QPS、错误率
  • 业务监控:订单量、支付成功率

系统监控

  • CPU、内存、磁盘、网络
  • 数据库监控:连接数、慢查询
  • 缓存监控:命中率、内存使用率

日志监控

  • 错误日志监控
  • 业务日志采集
  • 链路追踪

10.2 告警配置

应用告警

  • 接口错误率 > 1%
  • 响应时间 > 1秒
  • JVM内存使用率 > 80%

系统告警

  • CPU使用率 > 80%
  • 磁盘使用率 > 85%
  • 数据库连接数 > 80%

业务告警

  • 订单创建失败率 > 5%
  • 支付失败率 > 3%
  • 库存不足告警

十一、安全设计

11.1 数据安全

数据加密

  • 敏感数据加密存储(密码、手机号)
  • 传输数据使用HTTPS
  • 数据库字段加密

SQL注入防护

  • 使用预编译语句
  • 使用MyBatis等ORM框架
  • 输入参数校验

11.2 接口安全

认证授权

  • JWT Token认证
  • OAuth 2.0授权
  • 接口权限控制

防重放攻击

  • 时间戳校验
  • 随机数校验
  • 签名校验

限流防刷

  • 接口限流
  • 验证码校验
  • IP黑名单

十二、性能优化

12.1 数据库优化

索引优化

  • 为查询条件字段添加索引
  • 避免全表扫描
  • 使用覆盖索引

SQL优化

  • 避免SELECT *
  • 避免大表JOIN
  • 使用分页查询

连接池优化

  • 合理配置连接池参数
  • 监控连接泄漏

12.2 缓存优化

热点数据缓存

  • 用户信息、菜品信息缓存
  • 订单状态缓存
  • 配置信息缓存

缓存预热

  • 定时任务预热热点数据
  • 启动时加载配置数据

缓存更新策略

  • 写时更新缓存
  • 缓存失效策略

12.3 代码优化

异步处理

  • 非核心业务异步处理
  • 使用线程池或消息队列

批量操作

  • 数据库批量插入、更新
  • Redis批量操作

对象复用

  • 使用对象池
  • 避免频繁创建对象

十三、总结

外卖系统是一个复杂的分布式系统,涉及用户、商家、骑手三方角色,需要处理高并发、实时性、数据一致性等挑战。通过合理的架构设计、技术选型和性能优化,可以构建一个稳定、高效、可扩展的外卖平台。

核心要点

  • 微服务化架构,服务拆分合理
  • 数据库分库分表,支持水平扩展
  • 多级缓存体系,提升性能
  • 消息队列解耦,异步处理
  • 分布式事务,保证数据一致性
  • 监控告警,保障系统稳定
  • 安全防护,保护用户数据

未来演进方向

  • 智能化推荐:基于用户行为推荐菜品
  • 动态定价:根据供需关系动态调整价格
  • 无人配送:接入无人配送系统
  • 大数据分析:用户行为分析、经营分析

通过不断的技术演进和业务创新,外卖系统可以持续为用户提供更好的服务体验。

版权声明:本文为JienDa博主的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
若内容若侵犯到您的权益,请发送邮件至:platform_service@jienda.com我们将第一时间处理!
所有资源仅限于参考和学习,版权归JienDa作者所有,更多请访问JienDa首页。

给TA赞助
共{{data.count}}人
人已赞助
后端

面试官:单点登录怎么实现?我:你猜我头发怎么没的!

2025-12-23 9:57:22

后端

Kafka消息积压了,同事跑路了:从零开始的救火指南

2025-12-23 10:16:23

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索