1. 项目概述:当投票系统遇上微服务架构
去年参与某大型高校线上投票系统重构时,我们最初采用单体架构,结果在评选季高峰期系统频频崩溃。后来改用SpringCloud微服务方案,不仅扛住了单日300万投票请求,还实现了各院系独立部署的特殊需求。这个经历让我深刻体会到,现代投票系统早已不是简单的表单提交,而是需要应对高并发、防刷票、多租户等复杂场景的分布式系统。
这套基于SpringBoot+Vue+SpringCloud的在线投票系统,核心解决了三个痛点:一是通过服务拆分实现横向扩展,应对投票高峰期的流量冲击;二是前后端分离架构让移动端、Web端、小程序多端接入成为可能;三是SpringCloud生态提供的熔断、限流等机制保障了系统稳定性。对于需要组织线上评选、民意调查、活动投票的场景,这种架构既能快速响应业务变化,又能确保系统稳健运行。
2. 技术栈选型与架构设计
2.1 为什么选择SpringCloud微服务?
传统单体架构的投票系统在以下场景会暴露出明显缺陷:
- 投票活动开始瞬间的流量洪峰
- 不同业务模块的资源需求差异(如验证码服务CPU密集型,票数统计IO密集型)
- 需要针对部分功能单独扩容(如防刷票模块)
我们的微服务拆分方案:
mermaid复制graph TD
A[API Gateway] --> B[用户服务]
A --> C[投票服务]
A --> D[统计服务]
A --> E[防刷服务]
B --> F[MySQL集群]
C --> F
D --> G[Redis集群]
E --> G
实际部署时发现,将高频读写的票数统计与核心业务逻辑分离后,统计服务可以单独采用内存数据库+异步持久化策略,使TPS提升近8倍。SpringCloud Alibaba的Sentinel组件帮助我们实现了:
- 秒级精确的流量控制(如限制单个IP投票频率)
- 服务熔断机制(当Redis响应延迟超过阈值自动降级)
- 热点参数限流(针对特定候选人的突发投票量)
2.2 前后端分离的实践要点
前端采用Vue3+TypeScript的组合,其中三个关键设计值得注意:
-
长轮询优化:投票结果页不使用常规WebSocket,而是采用改良的长轮询策略。实测在万人在线场景下,这种方式比SSE节省40%的服务器资源。
-
防重复提交设计:
javascript复制// 前端防抖处理
const handleVote = debounce(async (candidateId) => {
if (votingStatus.value) return
votingStatus.value = true
try {
await submitVote(candidateId)
} finally {
votingStatus.value = false
}
}, 1000)
- 多端适配方案:通过BFF层(Backend for Frontend)统一接口格式,一套API同时支持:
- Web端(Vue实现复杂交互)
- 微信小程序(轻量级展示)
- 管理后台(React实现数据看板)
3. 核心业务逻辑实现
3.1 投票事务的分布式处理
投票业务看似简单的"读取-判断-写入"过程,在分布式环境下需要解决:
- 超卖问题(同一用户并发提交)
- 数据一致性问题(票数统计延迟)
我们的解决方案:
java复制// 使用Redis分布式锁+乐观锁组合
public VoteResult submitVote(Long userId, Long candidateId) {
String lockKey = "vote_lock:" + userId;
// 尝试获取锁,有效期3秒
boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 3, TimeUnit.SECONDS);
if (!locked) {
throw new BusinessException("操作太频繁");
}
try {
// 检查是否已投票
if (voteRecordMapper.exists(userId, voteId)) {
return VoteResult.fail("已参与过本次投票");
}
// 更新候选票数(带版本号校验)
int affected = candidateMapper.incrementVoteWithVersion(
candidateId,
voteRecordMapper.selectVersion(candidateId)
);
if (affected == 0) {
throw new ConcurrentVoteException("投票冲突,请重试");
}
// 记录投票行为
voteRecordMapper.insert(new VoteRecord(userId, candidateId));
return VoteResult.success();
} finally {
redisTemplate.delete(lockKey);
}
}
3.2 实时统计的优化策略
票数统计面临两个技术挑战:
- 高频更新对数据库的压力
- 实时性要求与系统性能的平衡
我们采用的混合方案:
- 内存计数:使用Redis INCR命令处理实时投票
- 异步落盘:通过RocketMQ延迟消息批量持久化
- 补偿机制:每小时执行一次全量校对
统计服务架构:
code复制[投票服务] --MQ--> [统计Worker] --批量写入--> [MySQL]
↑
[定时校对任务] ← [Redis Cluster]
实测数据:在候选人数50人、日均投票量200万的场景下,该方案使数据库写入压力降低92%,且统计延迟控制在3秒内。
4. 安全与防作弊设计
4.1 多层防御体系构建
-
行为验证层:
- 滑动验证码(活动开始前30分钟启用)
- 人机检测(鼠标轨迹分析)
-
规则引擎层:
java复制// 基于Groovy的动态规则
rule "异地登录检测"
when
$request : VoteRequest(ipChanged == true)
then
insert(new RiskEvent($request.userId, "IP变更异常"));
end
- 实时风控层:
- 基于Flink的实时行为分析
- 设备指纹识别(即使更换IP也能关联)
4.2 典型案例处理
某次校园歌手大赛中,我们发现了两种新型刷票方式:
- 时间规律破解:攻击者分析出验证码每5分钟变化的规律
- 解决方案:引入随机间隔变更策略
- 代理IP池轮换:检测到200+IP来自同一ASN
- 解决方案:结合IP信誉库+ASN分析
最终风控效果:
- 误杀率:<0.3%
- 作弊拦截率:98.6%
- 系统资源消耗增加:约15%(可接受)
5. 部署与性能调优
5.1 容器化部署方案
采用分层镜像构建策略:
dockerfile复制# 基础层
FROM openjdk:11-jre as runtime
# 应用层
COPY target/vote-service.jar /app/
# 配置层
VOLUME /app/config
# 健康检查
HEALTHCHECK --interval=30s CMD curl -f http://localhost:8080/actuator/health
Kubernetes部署要点:
- HPA配置:CPU>60%或内存>70%时自动扩容
- Pod反亲和性:确保同一服务的Pod分散在不同节点
- 资源限制:JVM堆内存设置为容器内存的70%
5.2 压测数据与优化
使用JMeter进行全链路压测时,发现三个性能瓶颈:
-
Nginx默认配置:
- 问题:worker_connections 1024不足
- 优化:调整为
worker_connections 8192;+multi_accept on;
-
SpringCloud Gateway:
- 问题:默认路由配置导致OOM
- 优化:增加过滤器
-Dreactor.netty.http.server.accessLogEnabled=true
-
Redis连接池:
- 问题:Lettuce客户端在高并发下连接泄漏
- 解决方案:配置
spring.redis.lettuce.pool.max-active=500
优化前后对比(单节点):
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 最大QPS | 1,200 | 3,800 |
| 平均延迟 | 450ms | 120ms |
| 错误率 | 8.7% | 0.2% |
6. 监控与运维实践
6.1 全链路监控体系
我们搭建的监控矩阵包含:
-
基础监控:Prometheus+Grafana采集
- JVM指标(GC次数、堆内存)
- 容器指标(CPU、内存、网络)
-
业务监控:
- 每分钟投票量变化
- 各候选人得票趋势
- 异常投票行为告警
-
日志分析:
- ELK收集关键日志
- 错误日志自动创建工单
6.2 典型故障处理
案例1:选票突然归零
- 现象:凌晨3点统计显示所有候选人票数清零
- 排查:
- 检查Redis集群状态正常
- 发现批量持久化任务日志异常
- 最终定位到MySQL连接池耗尽
- 解决方案:
- 增加连接池大小
- 添加补偿任务重试机制
- 实现双写校验逻辑
案例2:地域性访问失败
- 现象:华南地区用户无法提交投票
- 根因:CDN节点证书过期
- 改进:建立证书到期前15天自动提醒机制
7. 项目演进方向
在实际运行中,我们持续迭代了这些功能:
-
智能防刷升级:
- 引入机器学习模型识别异常模式
- 实现动态防御策略(攻击加剧时自动增强验证)
-
多维度统计分析:
- 选民 demographic 分析
- 投票时间分布热力图
- 候选人支持者画像
-
混合云部署方案:
- 核心服务部署在私有云
- 流量高峰时自动扩容到公有云
- 通过Service Mesh实现跨云通信
这套系统经过三个大型活动验证,最高记录单日处理:
- 注册用户:42万
- 有效投票:680万
- 峰值QPS:2,300
- 系统可用性:99.99%