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

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

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管理员
    暂无讨论,说说你的看法吧
❯
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索