1. 项目背景与核心价值
去年疫情期间,我参与开发了某三甲医院的疫苗预约系统。上线首日就遇到了3万并发请求,系统差点崩溃的经历让我深刻认识到:一个健壮的疫苗预约系统不仅需要基础功能,更要考虑极端场景下的稳定性。这个基于SpringBoot的疫苗预约系统解决方案,就是我在踩过无数坑后总结出的实战方案。
疫苗预约系统本质上是一个高并发、高可用的公共服务平台。它需要解决的核心问题包括:
- 如何在短时间内处理大量用户请求
- 如何防止黄牛刷号
- 如何保证数据一致性
- 如何应对突发流量
SpringBoot作为当前Java领域最流行的微服务框架,其自动配置、内嵌容器等特性特别适合快速构建此类系统。配合Redis缓存、RabbitMQ消息队列等技术栈,可以构建出能承受真实生产环境考验的解决方案。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计
2.1 技术选型决策
在技术选型阶段,我们对比了三种主流方案:
| 技术方案 | 开发效率 | 性能表现 | 学习成本 | 社区支持 |
|---|---|---|---|---|
| 纯Servlet | 低 | 高 | 高 | 一般 |
| Spring MVC | 中 | 高 | 中 | 丰富 |
| SpringBoot | 高 | 高 | 低 | 非常丰富 |
最终选择SpringBoot的原因:
- 自动配置大幅减少XML配置,开发效率提升40%以上
- 内嵌Tomcat简化部署,一行命令即可启动服务
- Starter依赖机制让技术整合变得极其简单
- Actuator提供完善的监控端点
2.2 分层架构设计
系统采用经典的三层架构,但针对疫苗预约场景做了特殊优化:
code复制表现层(Web)
├── 防刷模块(限流+验证码)
└── 静态资源CDN加速
业务逻辑层(Service)
├── 预约服务(分布式锁)
├── 库存服务(Redis缓存)
└── 消息服务(RabbitMQ)
数据访问层(Dao)
├── MyBatis动态SQL
└── 多数据源配置
关键优化点:
- 使用Redisson实现分布式锁,解决超卖问题
- 采用Redis Lua脚本保证库存操作的原子性
- 敏感操作全部记录操作日志
- 接口调用增加熔断降级策略
3. 核心功能实现
3.1 预约流程设计
疫苗预约的核心业务流程需要特别注意并发控制:
java复制// 伪代码展示关键逻辑
public Result bookVaccine(Long userId, Long vaccineId) {
// 1. 校验用户资格
if(!userService.checkQualification(userId)){
return Result.fail("不符合接种条件");
}
// 2. 获取分布式锁
RLock lock = redissonClient.getLock("vaccine:"+vaccineId);
try {
if(lock.tryLock(3, 10, TimeUnit.SECONDS)){
// 3. 检查库存
Integer stock = redisTemplate.opsForValue().get("stock:"+vaccineId);
if(stock <= 0){
return Result.fail("库存不足");
}
// 4. 扣减库存
redisTemplate.opsForValue().decrement("stock:"+vaccineId);
// 5. 创建订单
orderService.createOrder(userId, vaccineId);
// 6. 发送短信通知
mqService.sendSms(user.getPhone());
return Result.success();
}
} finally {
lock.unlock();
}
return Result.fail("系统繁忙,请重试");
}
3.2 高并发优化方案
在实际压力测试中,我们发现几个性能瓶颈点:
-
数据库连接池耗尽
- 解决方案:配置HikariCP连接池
yaml复制spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 -
缓存穿透问题
- 解决方案:布隆过滤器+空值缓存
java复制public Vaccine getVaccineById(Long id) { // 先查布隆过滤器 if(!bloomFilter.mightContain(id)){ return null; } // 再查缓存 String key = "vaccine:" + id; Vaccine vaccine = redisTemplate.opsForValue().get(key); if(vaccine == null) { // 查数据库 vaccine = vaccineMapper.selectById(id); if(vaccine != null){ redisTemplate.opsForValue().set(key, vaccine, 1, TimeUnit.HOURS); } else { // 缓存空值防止穿透 redisTemplate.opsForValue().set(key, new Vaccine(), 5, TimeUnit.MINUTES); } } return vaccine; } -
秒杀场景下的库存超卖
- 解决方案:Redis Lua脚本保证原子性
lua复制-- 库存扣减脚本 local stock = tonumber(redis.call('GET', KEYS[1])) if stock > 0 then redis.call('DECR', KEYS[1]) return 1 end return 0
4. 数据库设计要点
4.1 核心表结构
疫苗预约系统主要包含以下表:
sql复制-- 疫苗表
CREATE TABLE `vaccine` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '疫苗名称',
`manufacturer` varchar(100) DEFAULT NULL COMMENT '生产厂家',
`batch_number` varchar(50) DEFAULT NULL COMMENT '批次号',
`stock` int NOT NULL DEFAULT '0' COMMENT '库存数量',
`start_time` datetime DEFAULT NULL COMMENT '预约开始时间',
`end_time` datetime DEFAULT NULL COMMENT '预约结束时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 预约记录表
CREATE TABLE `appointment` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`vaccine_id` bigint NOT NULL,
`appoint_time` datetime NOT NULL COMMENT '预约时间',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '0-待接种 1-已接种 2-已取消',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user` (`user_id`),
KEY `idx_vaccine` (`vaccine_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4.2 分库分表策略
当预约量达到百万级别时,需要考虑分库分表:
- 垂直分库:将用户数据、预约数据、疫苗数据分离到不同库
- 水平分表:按用户ID哈希分片存储预约记录
- 全局ID生成:采用雪花算法(Snowflake)避免ID冲突
java复制// 雪花算法ID生成器配置
@Configuration
public class IdGeneratorConfig {
@Bean
public Snowflake snowflake() {
return new Snowflake(1, 1); // 数据中心ID和机器ID
}
}
5. 部署与监控方案
5.1 容器化部署
使用Docker Compose编排服务:
yaml复制version: '3'
services:
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
ports:
- "3306:3306"
volumes:
- ./mysql/data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
volumes:
- ./redis/data:/data
app:
build: .
ports:
- "8080:8080"
depends_on:
- mysql
- redis
5.2 监控配置
SpringBoot Actuator + Prometheus + Grafana监控方案:
- 启用Actuator端点
yaml复制management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
- Prometheus采集配置
yaml复制scrape_configs:
- job_name: 'vaccine-app'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['app:8080']
- Grafana监控看板关键指标:
- JVM内存使用
- 接口QPS/RT
- 数据库连接池状态
- Redis命中率
6. 安全防护措施
6.1 常见攻击防护
-
XSS防护:
- 前端:使用vue-sanitize过滤输入
- 后端:SpringBoot自动配置的Jackson会转义HTML
-
CSRF防护:
java复制@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); } } -
SQL注入防护:
- 使用MyBatis预编译语句
- 禁止拼接SQL字符串
6.2 预约防刷策略
-
限流措施:
java复制@RateLimiter(value = 10, key = "#userId") @PostMapping("/book") public Result bookVaccine(@RequestParam Long userId) { // 预约逻辑 } -
人机验证:
- 滑动验证码
- 短信验证码二次确认
-
黑名单机制:
- Redis记录异常请求IP
- 自动封禁高频请求用户
7. 开发环境搭建指南
7.1 基础环境准备
-
JDK 1.8+:
bash复制# 检查Java版本 java -version -
Maven 3.6+:
bash复制
mvn -v -
IDE推荐:
- IntelliJ IDEA (终极版)
- VS Code + Java插件
7.2 数据库初始化
-
创建数据库:
sql复制CREATE DATABASE vaccine_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -
导入初始数据:
bash复制
mysql -uroot -p vaccine_system < init_data.sql
7.3 项目导入与启动
-
克隆代码:
bash复制git clone https://github.com/example/vaccine-booking-system.git -
编译项目:
bash复制
mvn clean install -
启动应用:
bash复制
java -jar target/vaccine-booking-1.0.0.jar
在开发过程中,我强烈建议使用Lombok插件减少样板代码,并配置好Debug环境。遇到问题时,可以先检查以下几个方面:
- 数据库连接配置是否正确
- Redis服务是否正常运行
- 端口是否被占用
- 依赖版本是否有冲突
