1. 项目背景与核心价值
"苍穹外卖"这个命名很有意思,乍看像是某个外卖平台的内部项目代号。作为从业十年的老鸟,我见过太多类似的外卖系统开发案例。这类项目通常包含三个核心模块:用户端(小程序/APP)、商户后台、骑手调度系统。Day01的笔记往往聚焦在基础架构搭建和环境准备阶段。
从技术栈选择来看,2023年主流的外卖系统普遍采用微服务架构。Spring Boot + Spring Cloud的组合占比超过60%,其次是Node.js全栈方案。数据库方面MySQL仍是首选,但MongoDB在订单日志存储场景的使用率正在快速上升。
2. 开发环境配置实录
2.1 基础工具链选择
工欲善其事必先利其器,我的环境配置方案经过多个项目验证:
JDK选择:坚持使用LTS版本,目前Java17是平衡新特性和稳定性的最佳选择。注意设置JAVA_HOME时路径不要带空格。
IDE配置:
- IntelliJ IDEA 2023.2 Ultimate版
- 必备插件:Lombok、MyBatisX、Alibaba Java Coding Guidelines
- VM参数调优:-Xmx2048m -XX:ReservedCodeCacheSize=512m
数据库工具:
- 开发期用Docker跑MySQL 8.0
- 推荐使用DBeaver作为通用客户端
- 重要:character-set-server=utf8mb4必须设置
踩坑提醒:MySQL 8.0默认的caching_sha2_password认证方式会导致部分客户端连接失败,建议初始化时加上--default-authentication-plugin=mysql_native_password
2.2 项目骨架搭建
现代Java项目已经离不开构建工具。我的选择标准是:
- 新项目一律用Gradle(速度比Maven快30%)
- 多模块划分遵循"功能内聚"原则
- 关键依赖版本必须锁定
典型的外卖系统依赖配置示例:
ext { springBootVersion = '3.1.0' springCloudVersion = '2022.0.3' } dependencies { // 必选核心组件 implementation "org.springframework.boot:spring-boot-starter-web:$springBootVersion" implementation "org.springframework.cloud:spring-cloud-starter-openfeign:$springCloudVersion" // 持久层 implementation 'com.baomidou:mybatis-plus-boot-starter:3.5.3.1' runtimeOnly 'mysql:mysql-connector-java:8.0.33' // 工具类 compileOnly 'org.projectlombok:lombok:1.18.26' annotationProcessor 'org.projectlombok:lombok:1.18.26' }3. 领域模型设计要点
3.1 核心实体关系
外卖系统的领域模型有五个关键实体:
- 用户模型:需要区分C端用户和B端商户
- 商品模型:特别注意规格SKU的设计
- 订单模型:状态机设计是难点
- 配送模型:关联骑手和路线
- 支付模型:与第三方对接的防腐层设计
ER图核心字段示例(MySQL语法):
CREATE TABLE `order` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '雪花算法ID', `order_no` varchar(32) 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配送中 4已完成 5已取消', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;3.2 状态机设计模式
订单状态流转是外卖系统的核心逻辑。推荐使用状态模式(State Pattern)实现:
public interface OrderState { void pay(Order order); void accept(Order order); void deliver(Order order); void complete(Order order); void cancel(Order order); } // 具体状态实现示例 public class PaidState implements OrderState { @Override public void accept(Order order) { order.setState(new AcceptedState()); // 触发商家接单事件 eventPublisher.publishEvent(new OrderAcceptedEvent(order)); } // 其他方法抛出IllegalStateException }4. 技术难点解决方案
4.1 分布式ID生成
外卖系统对ID生成有三点要求:
- 全局唯一
- 趋势递增
- 高可用
Snowflake算法改良方案:
public class SequenceGenerator { private final long workerId; private long sequence = 0L; private long lastTimestamp = -1L; public synchronized long nextId() { long timestamp = timeGen(); if (timestamp < lastTimestamp) { throw new IllegalStateException("时钟回拨异常"); } if (lastTimestamp == timestamp) { sequence = (sequence + 1) & SEQUENCE_MASK; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - EPOCH) << TIMESTAMP_SHIFT) | (workerId << WORKER_SHIFT) | sequence; } }4.2 并发订单处理
解决超卖问题的三种方案对比:
| 方案 | 实现复杂度 | 性能影响 | 适用场景 |
|---|---|---|---|
| 数据库悲观锁 | ★★☆ | 高 | 低频高价值订单 |
| Redis分布式锁 | ★★★ | 中 | 中频常规订单 |
| 库存预扣减+MQ异步 | ★★★★ | 低 | 高频秒杀场景 |
推荐组合方案代码示例:
public boolean placeOrder(OrderDTO orderDTO) { // 1. Redis原子扣减 Long remain = redisTemplate.opsForValue() .decrement("stock:" + orderDTO.getSkuId()); if (remain < 0) { redisTemplate.opsForValue() .increment("stock:" + orderDTO.getSkuId()); throw new BusinessException("库存不足"); } // 2. 发送MQ消息 orderProducer.send(new Message( "order_topic", JSON.toJSONString(orderDTO).getBytes() )); return true; }5. 调试与性能优化
5.1 接口调试技巧
Postman高级用法三件套:
- 环境变量:区分dev/test/prod环境
- Tests脚本:自动处理token续期
if (pm.response.code === 401) { pm.sendRequest({ url: pm.variables.get("host") + "/auth/refresh", method: "POST", header: { "Authorization": "Bearer " + pm.variables.get("refresh_token") } }, (err, res) => { pm.variables.set("access_token", res.json().access_token); postman.setNextRequest(pm.info.requestName); }); }- Mock Server:前端并行开发时特别有用
5.2 JVM参数调优
外卖系统的JVM配置建议:
-server -Xms2g -Xmx2g -XX:NewRatio=2 -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=35 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/java_heapdump.hprof关键指标监控:
- GC日志分析:-Xloggc:/logs/gc.log -XX:+PrintGCDetails
- 内存泄漏检测:-XX:NativeMemoryTracking=detail
6. 持续集成方案
6.1 Git分支策略
推荐采用改良版Git Flow:
main - 生产环境代码(保护分支) release/* - 预发布分支 feature/* - 功能开发分支 hotfix/* - 紧急修复分支配合.gitignore文件:
# IDE .idea/ *.iml # Build target/ build/ # Logs *.log logs/ # Env .env *.properties6.2 Docker化部署
标准Dockerfile示例:
FROM eclipse-temurin:17-jdk-jammy WORKDIR /app COPY build/libs/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","app.jar"]多阶段构建优化版:
# 构建阶段 FROM maven:3.8.6-eclipse-temurin-17 AS build COPY . . RUN mvn clean package -DskipTests # 运行阶段 FROM eclipse-temurin:17-jre-jammy COPY --from=build /target/*.jar app.jar USER nobody ENTRYPOINT ["java","-jar","app.jar"]7. 安全防护措施
7.1 常见漏洞防护
外卖系统必须处理的六大安全问题:
SQL注入:
- 永远不要拼接SQL
- MyBatis使用#{}语法
- 启用mybatis-plus的防全表更新插件
XSS攻击:
@Configuration public class WebSecurityConfig { @Bean public FilterRegistrationBean xssFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new XssFilter()); registration.addUrlPatterns("/*"); return registration; } }CSRF防护:
- Spring Security默认启用
- 前后端分离项目需特殊处理
7.2 敏感数据保护
数据脱敏三原则:
- 存储加密:密码必须bcrypt哈希
- 传输加密:HTTPS+敏感字段RSA加密
- 展示脱敏:手机号显示为138****1234
Jasypt集成示例:
spring: datasource: url: jdbc:mysql://localhost:3306/db?useSSL=false username: root password: ENC(密文) jasypt: encryptor: password: ${JASYPT_PASSWORD} # 从环境变量读取8. 监控与告警体系
8.1 指标埋点方案
Micrometer + Prometheus配置:
@Bean public MeterRegistryCustomizer<PrometheusMeterRegistry> metrics() { return registry -> { registry.config().commonTags("application", "order-service"); new ClassLoaderMetrics().bindTo(registry); new JvmMemoryMetrics().bindTo(registry); }; }关键监控指标:
- 订单创建QPS
- 平均响应时间
- 错误率
- JVM内存使用率
8.2 日志收集规范
ELK最佳实践:
- 日志格式统一:
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern> - 添加TraceID:
@Bean public Filter traceIdFilter() { return (request, response, chain) -> { MDC.put("traceId", UUID.randomUUID().toString()); try { chain.doFilter(request, response); } finally { MDC.clear(); } }; }
9. 压测与性能调优
9.1 JMeter测试方案
典型外卖场景测试计划:
- 登录鉴权(获取token)
- 浏览商品(20%流量)
- 下单流程(60%流量)
- 订单查询(20%流量)
关键参数配置:
- 线程组:500并发,ramp-up 60秒
- 循环次数:永远循环
- 断言:响应时间<500ms,错误率<0.1%
9.2 性能瓶颈分析
Arthas常用命令:
watch com.example.service.OrderService createOrder '{params,returnObj,throwExp}' -x 3 trace com.example.service.OrderService createOrder profiler start --event cpu优化案例:MySQL批量插入
// 优化前:单条插入 orders.forEach(orderMapper::insert); // 优化后:批量插入 SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); OrderMapper mapper = session.getMapper(OrderMapper.class); orders.forEach(mapper::insert); session.commit();10. 项目演进路线
10.1 技术债管理
常见技术债清单:
- 硬编码配置(限期迁移到配置中心)
- 魔法数字(用枚举常量替代)
- 重复代码(提取公共模块)
- 裸SQL语句(改用MyBatis动态SQL)
10.2 架构演进方向
从单体到微服务的拆分策略:
- 第一阶段:按功能拆分(用户服务、订单服务)
- 第二阶段:按业务域拆分(C端服务、B端服务)
- 第三阶段:核心服务独立(支付服务、风控服务)
服务网格化改造路径:
- 接入Service Mesh
- 渐进式迁移流量
- 完善监控体系
- 优化服务治理
在真实项目中,Day01的成果应该包括:项目骨架搭建完成、基础依赖配置妥当、数据库脚本就绪、CI/CD流水线打通。这些基础工作看似简单,却直接影响后续开发效率。我见过太多项目因为初期架构没做好,后期不得不推倒重来。