一句话定位:2018 年那篇「从 0 到 1 构建分布式秒杀系统」的配套代码,把单机锁、数据库悲观锁与乐观锁、Redis 与 ZooKeeper 分布式锁、JVM 队列、Disruptor、Redis 订阅、Kafka 这些秒杀方案各写了一遍,放在同一个工程里对照着跑。它是教学案例,作者自己在 README 里写明「某些案例并不适用于生产环境」;仓库还在,但代码停在上一个时代(Spring Boot 1.5.10 + Swagger 2)。
| 项 | 值 |
|---|---|
| 仓库地址 | https://gitee.com/52itstyle/spring-boot-seckill |
| 项目定位 | 分布式秒杀系统的教学 Demo,一份代码演示多种并发控制方案 |
| 作者 / 站点 | 小柒2012(52itstyle),博客 https://blog.52itstyle.vip |
| 开源协议 | Apache-2.0(LICENSE 全文,Copyright 2018 小柒2012) |
| 后端技术栈 | Spring Boot 1.5.10 / Java 8 / Spring Data JPA / MySQL / Thymeleaf |
| 中间件 | Redis、ZooKeeper(Curator)、Kafka、ActiveMQ,Disruptor、Redisson |
| 前端形态 | Thymeleaf + NekoHTML + FreeMarker 静态化,页面用 iView / jQuery |
| 服务入口 | 8080 端口,contextPath /seckill,Swagger 在 /seckill/swagger-ui.html |
| 仓库状态 | 374 次提交,4 个分支(master、20200818、20180810、20180528),0 个标签 |
| 社区热度 | Gitee 14.5K Star / 6K Fork / 2.2K Watch / 24 Open Issues / 5 PR(2026-11-10 查询) |
| 荣誉 | Gitee GVP 项目 |
| 文档日期 | 2026-11-10 |
1. 项目是什么
它是一份「秒杀系统怎么做」的代码合集,不是一套能直接上线的秒杀平台。 同一个工程里放了好几条并行的实现路径:先用单机锁顶住,再换数据库锁,再换 Redis 分布式锁,再换 ZooKeeper,最后用消息队列把请求削峰。每种方案都能单独跑起来,访问对应的 Controller 就能对比效果。
理解这个仓库,先理解它的叙事结构:
| 层次 | 对应代码 | 解决的问题 |
|---|---|---|
| 业务模型 | Seckill、SuccessKilled 两张表 | 商品库存、秒杀成功记录(唯一索引防重复) |
| 单机并发 | lock/LockDemo、aop/LockAspect | synchronized、ReentrantLock、AOP 切面锁 |
| 数据库并发 | 悲观锁 for update、乐观锁版本号 | 不引中间件时的最朴素方案 |
| 分布式锁 | distributedlock/redis、distributedlock/zookeeper | 多机部署时锁必须跨进程 |
| 异步削峰 | queue/jvm、queue/disruptor、queue/redis、queue/kafka | 把「下单」从同步链路里摘出去 |
| 页面优化 | CreateHtmlServiceImpl + FreeMarker | 商品详情静态化,挡住读请求 |
| 入口防护 | 验证码、Guava 限流、Sentinel | 防刷、限流 |
需要先认清三点:
- 它是 2018 年的文章配套代码。README 里的架构讨论(高防 IP、SLB、Nginx 限流、DRDS 读写分离)是「应该怎么做」的讲解,仓库里的实现只是其中可演示的一小块。
- 每种方案都是独立 Demo,不是一套可切换的架构。代码里没有「策略模式选一种锁」这种设计,而是各写一个 Service 实现,你按需调用。
- 作者已经明确它不面向生产。README 原话:「本测试案例单纯为了学习,某些案例并不适用于生产环境」,「项目正在完善中(仅供参考)」。
2. 核心特性
| 特性 | 说明 | 学习价值 |
|---|---|---|
| 一张表讲清秒杀模型 | seckill 存库存与时间窗,success_killed 用联合主键防重复 | 秒杀的核心约束是「一人一单 + 库存不超卖」 |
| 三种单机锁对照 | synchronized、ReentrantLock、AOP 注解锁 | 直观看到锁粒度与吞吐的差别 |
| 数据库悲观锁 | select ... for update 锁行 | 理解行锁与事务边界 |
| 数据库乐观锁 | 版本号 CAS 更新,影响行数为 0 即失败 | 无锁方案的典型写法 |
| Redisson 分布式锁 | 封装好的可重入锁,带看门狗续期 | 生产里最常用的方案之一 |
| ZooKeeper 分布式锁 | Curator InterProcessMutex | 理解临时顺序节点与公平锁 |
| JVM 进程内队列 | LinkedBlockingQueue + 线程池消费 | 单机削峰的最小实现 |
| Disruptor 队列 | RingBuffer 无锁队列 | 高吞吐场景的经典组件 |
| Redis 订阅队列 | pub/sub 做异步通知 | 轻量消息通道 |
| Kafka 队列 | 生产/消费分离 | 真正的跨机削峰 |
| 页面静态化 | FreeMarker 生成商品详情 HTML | 读多写少场景的第一层优化 |
| 接口文档 | Swagger 2.7 在线调试 | 直接点页面就能复现并发效果 |
| 限流组件 | Guava RateLimiter、Sentinel | 演示「限流先于加锁」 |
3. 技术架构
整体结构:单体 Spring Boot 应用,内嵌 Tomcat,页面用 Thymeleaf 渲染,数据走 JPA,缓存与锁走 Redis,异步走队列,静态化走 FreeMarker。没有注册中心、没有网关,一切都在一个 jar 里。
| 层 | 技术 | 说明 |
|---|---|---|
| 框架 | Spring Boot 1.5.10.RELEASE | 2018 年初的版本,spring-boot-starter-parent 直接锁死 |
| Web | 内嵌 Tomcat | server.tomcat.max-threads=100,contextPath /seckill |
| 持久层 | Spring Data JPA + MySQL | mysql-connector-java 未锁版本,走 Boot 的依赖管理 |
| 模板 | Thymeleaf + NekoHTML | LEGACYHTML5 模式,允许不闭合标签 |
| 静态化 | FreeMarker | spring.freemarker.html.path 指定输出目录 |
| 缓存 / 会话 | spring-boot-starter-redis 1.4.7 + spring-session | 会话存 Redis,多机共享 |
| 分布式锁 | Redisson 2.11.5 / Curator 2.10.0 | Redis 与 ZooKeeper 两条路 |
| 消息 | spring-kafka 1.3.5、ActiveMQ、beanstalkc | 三种队列都留了示例 |
| 高吞吐队列 | Disruptor 3.4.1 | RingBuffer 实现 |
| 限流 | Guava 25.1、Sentinel 1.6.3 | 一个本地、一个可配规则 |
| 文档 | springfox-swagger2 2.7.0 | 老版 Swagger,路径 /swagger-ui.html |
| 监控 | spring-boot-starter-actuator | 管理端口单独开在 28806 |
| 工具 | Lombok、fastjson 1.2.83、commons-lang3 | 常规工具集 |
代码分层(com.itstyle.seckill):
| 包 | 职责 |
|---|---|
web | 三个入口:SeckillController(单机)、SeckillDistributedController(分布式)、CreateHtmlController(静态化) |
service / service.impl | 每种方案一个实现,SeckillServiceImpl、SeckillDistributedServiceImpl、CreateHtmlServiceImpl |
repository | JPA 仓储,SeckillRepository |
entity | Seckill、SuccessKilled、Result |
enums | SeckillStatEnum,秒杀状态与提示语 |
common.lock / common.aop | 锁示例与 @Servicelock 注解切面 |
distributedlock.redis | Redisson 配置、工具类与 Demo |
distributedlock.zookeeper | Curator 分布式锁工具 |
queue.jvm | 进程内队列 + TaskRunner 消费线程 |
queue.disruptor | 事件、工厂、生产者、消费者 |
queue.redis | 发布/订阅与消息监听容器 |
queue.kafka | 生产者与消费者 |
common.dynamicquery | 原生 SQL 动态查询封装 |
common.redis | Redis 配置与工具类 |
common.api | Swagger 配置 |
common.interceptor | 拦截器适配器 |
几个关键取舍:
- 用 JPA 而不是 MyBatis:Demo 里 SQL 简单,JPA 少写映射;代价是复杂查询要靠
dynamicquery那套原生 SQL 封装兜底。 - 一个工程塞多种方案:对照学习很方便,但依赖也因此变重——Kafka、ZooKeeper、ActiveMQ、Beanstalkd、腾讯云 SDK 全在 pom 里,实际跑某个 Demo 用不到这么多。
- 队列用四种实现:进程内、Disruptor、Redis、Kafka 各写一遍,是「教学」而不是「选型」,生产里只需要一种。
- 前端停留在 Thymeleaf + iView:2018 年的写法,今天看已经过时,但它不是这个项目的重点。
4. 目录结构
| 路径 | 用途 |
|---|---|
src/main/java/com/itstyle/seckill/Application.java | 启动类 |
src/main/java/.../web/ | 控制器:单机秒杀、分布式秒杀、静态页生成 |
src/main/java/.../service/ | 业务接口与各方案实现 |
src/main/java/.../distributedlock/ | Redis(Redisson)与 ZooKeeper(Curator)分布式锁 |
src/main/java/.../queue/ | 四套队列:jvm、disruptor、redis、kafka |
src/main/java/.../common/aop/ | 切面锁 LockAspect 与注解 Servicelock |
src/main/java/.../common/lock/ | 单机锁演示 |
src/main/java/.../common/entity/ | 实体:Seckill、SuccessKilled、Result |
src/main/resources/application.properties | 主配置:数据源、Redis、Kafka、ZooKeeper、验证码、Actuator |
src/main/resources/sql/ | 建表与初始化数据脚本 |
src/main/resources/templates/ | Thymeleaf 页面:商品列表、详情、结果页(1000–1003) |
src/main/resources/static/ | 前端资源:jQuery、iView、商品图、详情页样式 |
src/main/resources/template/goods.flt | FreeMarker 静态化模板 |
src/main/resources/redis/ | Windows 版 Redis 压缩包,方便本地跑 |
pom.xml | 依赖清单,Spring Boot 1.5.10 |
数据库/、服务器/、架构之路/ | 作者整理的文档与配图,不是可运行代码 |
Java Basis/ | Java 基础示例,与秒杀主流程无关 |
README.md | 项目说明、架构讲解、参考文章列表 |
仓库里混进了大量非代码内容(HTML 与图片占了语言统计的三分之一),clone 下来会比预期大。redis/ 目录直接放了 Windows 版 Redis 压缩包,这在开源仓库里不常见,也解释了为什么它能在 Windows 上「开箱跑」。
5. 快速上手
前置条件
JDK 8、Maven、MySQL 5.7/8.0、Redis。要跑分布式 Demo,还得有 ZooKeeper 3.4.6 与 Kafka。作者建议在 Linux 下装这些中间件。
第一步:建库导数据
mysql -uroot -p
# 登录后依次执行
CREATE DATABASE seckill DEFAULT CHARSET utf8mb4;
USE seckill;
source src/main/resources/sql/seckill.sql如果脚本路径或文件名与仓库当前版本不一致,以 src/main/resources/sql 下的实际文件为准。
第二步:改配置
application.properties 里至少要改这几处:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/seckill?characterEncoding=utf-8
spring.datasource.username=你的账号
spring.datasource.password=你的密码
spring.redis.host=127.0.0.1
spring.redis.port=6379
zookeeper.address=127.0.0.1:2181
spring.kafka.bootstrap-servers=127.0.0.1:9092只跑单机 Demo 的话,ZooKeeper 与 Kafka 的地址可以不管,但相关 Bean 在启动时可能仍会尝试连接,遇到报错就把对应 Controller 与队列类先注释掉。
第三步:启动
mvn clean package -DskipTests
java -jar target/spring-boot-seckill-0.0.1-SNAPSHOT.jar或者在 IDEA 里直接跑 Application 的 main 方法。
第四步:验证
- 接口调试:
http://localhost:8080/seckill/swagger-ui.html - 秒杀商品页:
http://localhost:8080/seckill/index.shtml(README 注明部分功能待完成) - 管理端口:
http://localhost:28806,Actuator 端点开在这里
用工具压一下
页面点按钮只能验证逻辑,要看并发效果得用压测工具。作者文章里用的是 JMeter 一类工具,对同一个秒杀地址并发请求,观察 success_killed 表的行数是否等于库存。这一步才是这个项目真正的价值所在——看不同方案在同样的并发下,超卖与吞吐差多少。
6. 功能模块清单
| 模块 | 能做什么 | 对应代码 |
|---|---|---|
| 秒杀列表 | 展示进行中与即将开始的商品 | templates/index.html |
| 商品详情 | 库存、时间、秒杀按钮 | templates/1000.html 等 |
| 单机秒杀 | synchronized 或 ReentrantLock 扣库存 | SeckillServiceImpl |
| AOP 切面锁 | 注解式加锁,业务代码无侵入 | LockAspect + @Servicelock |
| 数据库悲观锁 | for update 锁行 | SeckillServiceImpl 悲观锁方法 |
| 数据库乐观锁 | 版本号 CAS 更新 | SeckillServiceImpl 乐观锁方法 |
| Redis 分布式锁 | Redisson 加锁、解锁 | RedissLockUtil |
| ZooKeeper 分布式锁 | Curator 互斥锁 | ZkLockUtil |
| 进程内队列 | 请求入队,后台线程扣库存 | SeckillQueue + TaskRunner |
| Disruptor 队列 | RingBuffer 事件流转 | SeckillEventProducer / Consumer |
| Redis 队列 | 发布订阅异步处理 | RedisSender / RedisConsumer |
| Kafka 队列 | 跨机削峰 | KafkaSender / KafkaConsumer |
| 静态化 | 生成商品详情静态 HTML | CreateHtmlServiceImpl |
| 验证码 | 腾讯云验证码防刷 | 配置项 qq.captcha.* |
| 限流 | Guava RateLimiter、Sentinel | common 下示例 |
| 通知 | WebSocket 推送秒杀结果 | spring-boot-starter-websocket |
7. 版本与授权
| 项 | 实际情况 |
|---|---|
| 版本号 | 没有版本号。pom.xml 里是 0.0.1-SNAPSHOT,仓库 0 个 Git 标签 |
| 分支 | 4 个:master、20200818、20180810、20180528,按日期命名 |
| 许可证 | Apache-2.0,LICENSE 全文,版权行写「Copyright 2018 小柒2012」 |
| 维护状态 | README 自述「项目正在完善中(仅供参考)」;代码主体停留在 2018–2020 年 |
| 荣誉 | Gitee GVP |
Apache-2.0 对使用者的意义:
- 可以商用、闭源修改、对外分发,只需保留版权与许可声明,修改过的文件加注说明;
- 带专利授权条款;
- 作者不提供任何担保,「按现状」提供。
但要注意:协议的宽松不代表代码可以直接上生产。这份代码的价值在于演示并发控制思路,业务完整性(支付回调、超时未支付释放库存、退款、风控)并不在其中。
8. 商业模式说明
这个仓库本身不卖钱,Apache-2.0 免费。它的作用有两个:
- 技术内容引流。作者博客(52itstyle.vip)围绕这个项目写了一整个系列:分布式锁、限流、Disruptor、静态化、验证码、重复下单、WebSocket 通知,每篇都对应仓库里的一段代码。这套内容为作者积累了 GVP 与 14.5K Star。
- 给商业产品导流。现在打开 README,最上面一整屏是多商户商城源码的销售广告(小柒商城:S2B2C、B2B2C、B2C、O2O 多模式,含装修、分账、直播、拼团等),往下翻很久才看到原来的秒杀内容。仓库简介也被改成了「Java 商城框架,商城开发事半功倍」。
也就是说,这个仓库现在的第一身份是商业商城源码的展示位,第二身份才是秒杀教学案例。判断它是否还值得参考时,看代码提交而不是看 README 首屏。
变现路径也很典型:个人 IP(博客、公众号、QQ 群)+ 源码授权 + 二开定制。这在国产 Java 开源圈里是最常见的一种模式。
9. 生态与周边
| 项目 / 资源 | 作用 | 地址 |
|---|---|---|
| 作者博客 | 秒杀系列文章,逐篇对应代码 | blog.52itstyle.vip |
| SPTools | 作者的极简开发工具箱 | gitee.com/52itstyle/SPTools |
| spring-boot-pay | 支付服务示例 | gitee.com/52itstyle/spring-boot-pay |
| spring-boot-quartz | 任务调度示例 | gitee.com/52itstyle/spring-boot-quartz |
| spring-boot-sharding-sphere | 分库分表、读写分离案例 | gitee.com/52itstyle/spring-boot-sharding-sphere |
| spring-boot-elasticsearch | 搜索服务示例 | gitee.com/52itstyle/spring-boot-elasticsearch |
| 抢红包案例 | 高并发抢红包、延迟队列 | 作者博客 archives 系列 |
| 交流群 | QQ 群 933593697 | — |
| API 文档 | Gitee ApiDoc 页 | apidoc.gitee.com/52itstyle/spring-boot-seckill |
生态上要说清楚:没有官方镜像站、没有 Docker 镜像、没有 Helm,也没有配套的前端工程。它是一个「代码 + 文章」的组合,周边全靠作者博客串联。
10. 选型建议
适合你,如果:
- 你想系统搞懂秒杀里各种锁与队列的差别,需要能跑的对照代码;
- 你在准备面试或做技术分享,需要一套能把「超卖」「重复下单」「削峰」讲清楚的示例;
- 你要给团队做并发编程培训,希望有现成的 Demo 改;
- 你在评估某个方案(比如 Redisson 与 Curator 哪个更顺手),想先跑个最小验证。
不适合你,如果:
- 你要一套能上线的秒杀/抢购系统——业务完整性远远不够;
- 你的项目已经是 Spring Boot 2.x/3.x,这份 1.5.10 的代码没法直接搬,只能抄思路;
- 你不想装 Kafka、ZooKeeper 这一堆中间件;
- 你需要有人维护、有版本、有 changelog 的依赖。
和同类怎么比:市面上讲秒杀的仓库大多只有一种实现(多半是 Redis + Lua 扣库存)。这个项目的优势是横向铺得全,能在一套代码里横向对比;劣势是纵向都不深,每个方案都是 Demo 级,缺生产细节(幂等、补偿、对账、降级)。把它当教材,不要当脚手架。
11. 风险与坑
- 它明确不是生产系统:README 自己写「某些案例并不适用于生产环境」「仅供参考」。拿它上线,支付、退款、库存回滚、风控全都要自己补。
- 配置文件里是明文的默认口令:
spring.datasource.username=root、password=root,Redis 密码注释掉默认空。改配置是第一件要做的事。 - 内网 IP 写死在配置里:Kafka 的
bootstrap-servers与 ZooKeeper 的address默认指向192.168.1.180,这个地址是作者当时的实验环境,直接启动会一直重连。 - Actuator 的关机端点开着:
endpoints.shutdown.enabled=true、management.security.enabled=false、管理端口 28806 独立暴露。这意味着任何能访问 28806 的人都能把服务关掉。这是 Spring Boot 1.5 时代最典型的安全坑,务必关掉shutdown端点并加访问控制。 - JPA 的
ddl-auto=update:启动时按实体自动改表结构,本地方便,生产上随时可能改坏索引或字段。生产必须改成none并用 SQL 迁移。 - 静态页输出路径写死 Windows 盘符:
spring.freemarker.html.path = D://file//,Linux 上要改成实际目录,否则静态化功能直接报错。 - Spring Boot 1.5.10 早已结束维护:Spring Framework 4.3 分支的已知漏洞不会再修,内嵌 Tomcat 版本也很老。要用于任何对外服务,先把框架升级评估做掉——但升级基本等于重写,1.5 到 2.x 的配置项、Actuator、安全模型都变了。
- 依赖版本互相打架:
spring-boot-starter-redis锁在 1.4.7(Boot 1.5 时代已被spring-boot-starter-data-redis取代)、spring-kafka 1.3.5、Curator 2.10 对应 ZooKeeper 3.4.6。这些版本必须成套使用,单独升一个大概率启动失败。 - 0 个标签、分支按日期命名:想追某个时间点的代码只能按分支名猜,没有 changelog,也没有 release 包。
- README 首屏已是商业广告:判断项目状态请看提交记录与代码,别被首屏的商城宣传误导成「这是个商城系统」。
- 仓库体积偏大:
架构之路/、数据库/、服务器/三个目录塞了大量图片与文档,static/里还有商品图,clone 会比预期慢。 - 验证码依赖腾讯云:
qq.captcha.*四个参数是作者账号的掩码值,要自己申请腾讯云验证码服务并换掉,否则防刷环节跑不通。 - Demo 只覆盖「抢到」这一步:下单成功后的支付、超时关单、库存回补、通知,README 的「思考改进」里自己列成了待办。做业务时这些才是主要工作量。
- 压测结论不能外推:本机 Redis/单机 MySQL 下的 QPS 数字没有参考意义,换环境就变。要评估得在自己的目标环境压。
12. 参考链接
本文基于 2026-11-10 的 master 分支源码(pom.xml、application.properties、README、目录结构)、Gitee 项目页与作者博客整理;Star 数、分支与标签数量均以该日为准。项目为教学案例,代码与文章存在时间差,落地前请以仓库实际内容为准。