1. 项目背景与核心价值
去年参与了一个疫情防控系统的开发,用SpringBoot技术栈实现了从数据采集到可视化分析的全流程管理。这个系统在特殊时期发挥了重要作用,日均处理10万+条健康上报数据,支持200+个社区同时在线使用。不同于简单的CRUD系统,我们针对疫情防控的特殊需求做了大量定制开发,今天就来拆解其中的关键技术点和实战经验。
疫情防控系统的核心价值在于三点:实时性(数据秒级更新)、准确性(避免人工统计误差)和可追溯性(完整记录人员流动轨迹)。传统Excel表格管理方式在这三个方面都存在明显短板,而我们的系统通过技术手段完美解决了这些痛点。
2. 技术架构设计
2.1 整体技术选型
采用经典的SpringBoot+MyBatisPlus后端架构,前端使用Vue+ElementUI。特别选择了以下技术组件:
- 数据缓存:Redis集群(处理突发高并发访问)
- 消息队列:RabbitMQ(异步处理数据上报)
- 文件存储:MinIO(替代FastDFS,更易部署)
- 权限控制:Sa-Token(轻量级鉴权方案)
技术选型心得:在疫情场景下,系统的稳定性和快速迭代能力比技术新颖性更重要。我们放弃了微服务架构,采用单体应用+集群部署的方式,大幅降低了运维复杂度。
2.2 数据库设计要点
核心表结构设计考虑了疫情防控的特殊需求:
CREATE TABLE `health_report` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_id` varchar(32) NOT NULL COMMENT '加密身份证号', `temperature` decimal(3,1) NOT NULL, `symptom` varchar(255) DEFAULT NULL COMMENT '症状JSON数组', `location` point NOT NULL COMMENT 'GPS坐标', `is_risk` tinyint(1) DEFAULT '0', `report_time` datetime NOT NULL, PRIMARY KEY (`id`), SPATIAL KEY `idx_location` (`location`), KEY `idx_user_time` (`user_id`,`report_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;特别注意:
- 地理位置使用MySQL的GIS功能实现电子围栏判断
- 身份证号采用SM4加密存储
- 症状字段使用JSON格式适应动态字段需求
3. 核心功能实现
3.1 高并发上报接口
疫情爆发期单日峰值QPS达到3000+,我们通过三级缓存实现稳定服务:
- 本地缓存(Caffeine):缓存最近5分钟的热点小区数据
- Redis集群:存储当天所有上报数据的摘要信息
- MySQL分库分表:按用户ID哈希分16个库
关键代码示例:
@RestController @RequestMapping("/api/report") public class ReportController { @RateLimiter(value = 5000, key = "#communityCode") @PostMapping("/health") public Result submitHealth(@Valid @RequestBody HealthDTO dto) { // 1. 异步写入消息队列 rabbitTemplate.convertAndSend("health.queue", dto); // 2. 更新Redis实时统计 redisTemplate.opsForZSet().incrementScore( "realtime:count", dto.getCommunityCode(), 1 ); return Result.success(); } }3.2 智能预警模块
实现三个维度的预警规则:
- 时空碰撞:同一地点15分钟内超过阈值人数上报
- 轨迹重合:与确诊患者轨迹时空交集判断
- 症状聚类:相似症状人群地理分布分析
使用Elasticsearch实现轨迹查询优化:
BoolQueryBuilder query = QueryBuilders.boolQuery() .must(QueryBuilders.termQuery("userId", confirmedUserId)) .must(QueryBuilders.rangeQuery("reportTime") .gte(startTime) .lte(endTime)); SearchRequest request = new SearchRequest("trajectory") .source(new SearchSourceBuilder() .query(query) .size(10000));4. 性能优化实践
4.1 热数据预加载方案
每天凌晨3点执行预热任务:
- 预加载前7天确诊患者轨迹到Redis
- 生成各小区风险等级缓存
- 预计算统计报表数据
使用Spring Scheduler实现:
@Scheduled(cron = "0 0 3 * * ?") public void preloadHotData() { // 1. 查询近7天确诊患者 List<ConfirmedCase> cases = caseMapper.selectRecentCases(7); // 2. 批量写入Redis cases.forEach(case -> { redisTemplate.opsForValue().set( "confirmed:" + case.getUserId(), case.getTrajectory() ); }); }4.2 数据库查询优化
针对三大慢查询进行专项优化:
- 轨迹碰撞查询:添加复合索引 (user_id, report_time)
- 统计报表查询:使用物化视图预计算
- 模糊搜索:改用ES替代LIKE查询
优化前后对比:
| 查询类型 | 优化前耗时 | 优化后耗时 | 优化手段 |
|---|---|---|---|
| 轨迹碰撞 | 1200ms | 80ms | 空间索引+内存计算 |
| 日报统计 | 3000ms | 200ms | 预聚合+定时刷新 |
| 人员搜索 | 800ms | 50ms | ES倒排索引 |
5. 安全防护措施
5.1 数据加密方案
敏感字段采用国密算法处理:
- 身份证号:SM4加密存储
- 地理位置:GeoHash编码后AES加密
- 传输数据:HTTPS+自定义报文加密
加密工具类示例:
public class CryptoUtils { private static final SM4 sm4 = new SM4("系统密钥".getBytes()); public static String encryptIdCard(String idCard) { return Base64.encode(sm4.encrypt(idCard.getBytes())); } public static String decryptIdCard(String cipherText) { return new String(sm4.decrypt(Base64.decode(cipherText))); } }5.2 防攻击策略
针对常见攻击手段的防护方案:
- 上报防刷:IP+设备指纹限流
- 接口防护:请求签名+时效验证
- 数据篡改:区块链存证关键操作
安全拦截器配置:
public class SecurityInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 1. 校验时间戳 long timestamp = Long.parseLong(request.getHeader("X-Timestamp")); if (System.currentTimeMillis() - timestamp > 5000) { throw new ApiException("请求已过期"); } // 2. 验证签名 String sign = request.getHeader("X-Signature"); String rawData = request.getQueryString() + timestamp; if (!verifySign(rawData, sign)) { throw new ApiException("签名验证失败"); } return true; } }6. 运维监控体系
6.1 全链路监控方案
采用Prometheus+Grafana搭建监控看板,重点监控:
- 服务健康度:接口成功率、响应时间
- 资源使用率:CPU、内存、磁盘IO
- 业务指标:上报量、预警数、处理时效
关键指标配置示例:
# prometheus.yml 配置片段 scrape_configs: - job_name: 'springboot' metrics_path: '/actuator/prometheus' static_configs: - targets: ['app1:8080', 'app2:8080']6.2 日志分析优化
通过ELK实现日志集中管理,特别注意:
- 敏感信息脱敏处理
- 错误日志自动告警
- 操作日志审计追踪
Logback配置示例:
<appender name="ELK" class="net.logstash.logback.appender.LogstashTcpSocketAppender"> <destination>logstash:5044</destination> <encoder class="net.logstash.logback.encoder.LogstashEncoder"> <customFields>{"app":"covid-system","env":"prod"}</customFields> </encoder> </appender>7. 典型问题排查
7.1 内存泄漏问题
现象:服务运行3天后出现OOM 排查过程:
- 通过jmap生成堆转储文件
- 使用MAT分析发现Redis连接未释放
- 定位到未正确关闭的Jedis实例
修复方案:
// 错误写法 public void queryRedis() { Jedis jedis = pool.getResource(); // 业务代码... // 忘记调用jedis.close() } // 正确写法 public void queryRedis() { try (Jedis jedis = pool.getResource()) { // 业务代码... } }7.2 缓存雪崩应对
预防措施:
- 差异化过期时间:基础缓存时间+随机偏移量
- 多级缓存:本地缓存+分布式缓存
- 熔断降级:Hystrix保护核心服务
缓存工具类改进:
public class CacheHelper { private static final Random random = new Random(); public static void setWithRandomTTL(String key, Object value, int baseTTL) { int realTTL = baseTTL + random.nextInt(300); // 5分钟随机偏移 redisTemplate.opsForValue().set( key, value, realTTL, TimeUnit.SECONDS ); } }这个项目让我深刻体会到,应急系统的开发必须平衡好三个要素:快速响应需求变更的灵活性、突发流量下的稳定性、敏感数据的安全性。我们在迭代过程中总结出一套"小步快跑+灰度发布"的开发模式,通过自动化测试和监控告警体系保障系统稳定运行。