techa03 / goodsKill

🐉基于SpringCloud 2022.x +Dubbo 3.x构建的模拟秒杀微服务项目,集成了Elasticsearch🔍、Gateway、Mybatis-Plus、Sharding-JDBC等常用开源组件

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

前言

License Java CI with Maven codecov CodeQL

项目命名为 goodsKill 一方面有商品秒杀的意思(好像有点chinglish的味道),另外也可理解为 good skill,本项目就是希望搭建一套完整的项目框架,把一些好的技术和开发技巧整合进来(偏向于后端技术),方便学习和查阅。

本项目为模拟秒杀项目,提供统一秒杀模拟请求接口,方便自行测试各个秒杀方案的执行速度。 技术上整体采用SpringMVC + Mybatis持久层框架,采用Dubbo3.x[1],服务注册发现以及配置中心使用Nacos,支持数据库分库分表、分布式事务,使用状态机完成数据状态间的转换(基于Spring Statemachine实现)。

💎 分支介绍

master分支基于最新Spring Cloud 2022.x+ Spring Boot 3.x + JDK17体系构建,目前仅保留核心的模拟秒杀API接口,如需使用Spring Boot 2.7.x + JDK11版本可以切换到tag v2.7.4(支持登录注册以及简单的后台管理功能)。master分支目前升级改造中,功能不够稳定,如遇代码报错建议使用老版本。

本项目功能目前比较简陋且有很多不完善的地方,真实的秒杀场景远比本项目中的实现方式复杂,本项目省略了真实场景中的部分技术实现细节,目前仅作学习参考之用,如果觉得本项目对你有帮助的请多多star支持一下👍~~。

附:码云项目链接 https://gitee.com/techa/goodsKill,clone速度慢的用码云仓库拉吧,不定期同步到码云~

✨ 技术选型

使用的工具或框架 名称 官网

Spring Boot

Spring Boot框架

https://spring.io/projects/spring-boot

MyBatis-Plus

MyBatis增强工具

https://mp.baomidou.com/

ZooKeeper

分布式协调服务

http://zookeeper.apache.org/

Redis

分布式缓存数据库

https://redis.io/

Kafka

消息队列

http://kafka.apache.org/

RabbitMQ

消息队列

https://www.rabbitmq.com/

MongoDB

Mongo数据库

https://www.mongodb.com/

MySQL

MySQL数据库

https://www.mysql.com/

Elasticsearch

全文搜索引擎

https://www.elastic.co

Sharding-JDBC

分库分表组件

https://shardingsphere.apache.org

Spring Cloud Alibaba

Cloud Alibaba组件

https://github.com/alibaba/spring-cloud-alibaba

Apache Dubbo

RPC服务远程调用框架

https://github.com/apache/dubbo

Spring Cloud Gateway

网关组件

https://spring.io/projects/spring-cloud-gateway

Kotlin

Kotlin

https://kotlinlang.org/

Seata

分布式事务解决方案

http://seata.io/zh-cn/index.html

Spring Security OAuth2.0

OAuth2.0授权组件

https://spring.io/projects/spring-security-oauth

Canal

基于MySQL数据库的增量日志解析工具

https://github.com/alibaba/canal

GraphQL

一种用于 API 的查询语言

https://docs.spring.io/spring-graphql/docs/current/reference/html

Spring Statemachine

Spring 状态机

https://spring.io/projects/spring-statemachine

📝 项目模块介绍

goodsKill
|--goodskill-admin                          ||SpringBoot Admin监控服务端,支持Spring Cloud微服务发现
|--goodskill-canal                          ||用于完成MySQL数据库binlog收集的canal客户端程序
|--goodskill-gateway                        ||微服务网关,支持动态路由加载
|--goodskill-order-provider                 ||订单服务提供者
|   |--goodskill-order-service
|--goodskill-seckill-provider               ||秒杀服务提供者
|   |--goodskill-api                        ||提供服务API接口
|   |--goodskill-service                    ||服务API接口实现
|--goodskill-spring-boot-starter            ||项目配置自动装配
|--goodskill-web                            ||提供秒杀模拟接口访问
|--goodskill-job                            ||elastic-job定时任务
|--goodskill-seata                          ||集成nacos+dubbo+shardingjdbc+seata的分布式事务解决方案示例
|--goodskill-auth                           ||auth登录以及授权模块
|   |--auth-service                         ||基于本项目用户体系的用户登录授权服务
    |--oauth2-auth-server                   ||oauth2.0登录授权服务端,自定义的登录授权服务
    |--oauth2-resource-server               ||oauth2.0资源服务端,自定义的登录授权服务

🔥🔥 秒杀方案

目前实现了几种秒杀方案,通过SeckillMockController提供测试接口

聚合网关Openapi文档地址: http://localhost/doc.html#/home(需要开启网关服务)

Spring Boot Admin应用监控地址: http://www.goodskill.com:19031, 登录用户名密码:user/123456

  • 场景一:Sychronized同步锁实现

  • 场景二:Redisson分布式锁实现

  • 场景三:ActiveMQ实现(已废弃)

  • 场景四:Kafka消息队列实现

  • 场景五:数据库原子性更新

  • 场景六:实时等待秒杀处理结果(已废弃)

  • 场景七:ZooKeeper分布式锁

  • 场景八:使用Redis进行秒杀商品减库存操作,秒杀结束后异步发送MQ,使用MongoDB完成数据落地

  • 场景九:Spring Cloud Stream实现

  • 场景十:Sentinel限流+数据库原子性更新(需搭配sentinel控制台配置资源名limit的流控规则)

  • 场景十一:数据库原子性更新 + Canal数据库binlog日志监听秒杀结果

可在web控台查看秒杀结果,打印信息类似:
2021-04-14 21:58:59.857  INFO [goodskill-web,df43cc8f59291c48,df43cc8f59291c48] 15808 --- [           main] o.s.w.controller.SeckillMockController   : 秒杀场景二(redis分布式锁实现)开始时间:Wed Apr 14 21:58:59 CST 2021,秒杀id:1000
2021-04-14 21:59:00.094  INFO [goodskill-web,144aa7910cca9520,2821cb8d62c5a908] 15808 --- [AClOSzbugzYng-1] o.s.w.s.c.SeckillMockResponseListener    : 秒杀活动结束,秒杀场景二(redis分布式锁实现)时间:Wed Apr 14 21:59:00 CST 2021,秒杀id:1000
2021-04-14 21:59:00.101  INFO [goodskill-web,144aa7910cca9520,2821cb8d62c5a908] 15808 --- [AClOSzbugzYng-1] o.s.w.s.c.SeckillMockResponseListener    : 最终成功交易笔数统计中。。。
2021-04-14 21:59:01.616  INFO [goodskill-web,144aa7910cca9520,2821cb8d62c5a908] 15808 --- [AClOSzbugzYng-1] o.s.w.s.c.SeckillMockResponseListener    : 最终成功交易笔数统计中。。。
2021-04-14 21:59:03.129  INFO [goodskill-web,144aa7910cca9520,2821cb8d62c5a908] 15808 --- [AClOSzbugzYng-1] o.s.w.s.c.SeckillMockResponseListener    : 最终成功交易笔数:10
2021-04-14 21:59:03.130  INFO [goodskill-web,144aa7910cca9520,2821cb8d62c5a908] 15808 --- [AClOSzbugzYng-1] o.s.w.s.c.SeckillMockResponseListener    : 历史任务耗时统计:StopWatch '': running time = 36159894800 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
4492195700  012%  秒杀场景四(kafka消息队列实现)
3164155900  009%  秒杀场景八(秒杀商品存放redis减库存,异步发送秒杀成功MQ,mongoDb数据落地)
6219218300  017%  秒杀场景十(Sentinel限流+数据库原子性更新)
9189080600  025%  秒杀场景七(zookeeper分布式锁)
3135926500  009%  秒杀场景五(数据库原子性更新update set num = num -1)
3342791800  009%  秒杀场景九(基于springcloud stream rabbitmq)
3343433700  009%  秒杀场景一(sychronized同步锁实现)
3273092300  009%  秒杀场景二(redis分布式锁实现)

🧰 开发环境版本说明

  • JDK: OpenJDK17

  • Sharding-JDBC: 5.3.2

  • SpringCloud: 2022.x.x

  • SpringBoot: 3.x.x

  • SpringCloudAlibaba: 2022.x.x

  • Apache Dubbo: 3.x

  • Kotlin: 1.7.x

  • 使用的Docker镜像

    镜像 版本 端口 用户名密码

    Nacos

    2.2.3

    8848

    nacos:nacos

    Redis

    latest

    6379

    密码:123456

    Kafka

    3.1.1

    9092

    KafkaManager

    latest

    9001:9000

    Mongo

    6.0.7

    27017

    MySQL

    8.0.29

    3306

    root:Password123

    Zookeeper

    3.6.2

    2181

    Elasticsearch

    7.17.3

    9200 9300

    Kibana

    7.17.3

    5601

    RabbitMQ

    latest

    5672 15672

🎯 快速开始

  • 项目根目录goodsKill中执行

      mvn clean install
      或
      #跳过单元测试
      mvn clean install -DskipTests
  • 默认端口启动nacos、redis、mysql、rabbitmq、kafka、zookeeper、elasticsearch,或者使用docker-compose[2]命令:

      docker-compose -f goodskill-simple.yml up -d
  • 进入goodskill-web/src/main/sql目录,找到seckill.sql文件,在本地mysql数据库中建立seckill仓库并执行完成数据初始化操作

    ⚠️
    docker-compose启动MySQL镜像时会自动执行初始化脚本,如已执行过上一步本步骤可跳过
  • 配置host

     127.0.0.1       kafka
     127.0.0.1       nacos
     127.0.0.1       redis
     127.0.0.1       mysql
     127.0.0.1       zookeeper
     127.0.0.1       mongo
     127.0.0.1       elasticsearch
     127.0.0.1       rabbitmq
     127.0.0.1       logstash
     ##如果网关服务部署在远程机器,此处改为相应的远程机器ip
     127.0.0.1       www.goodskill.com
  • main方法运行OrderApplication类(订单服务)

  • main方法运行SeckillApplication类(秒杀管理服务提供者)

  • main方法运行SampleWebApplication类(模拟秒杀web服务)

  • 启动完成后发送一个秒杀模拟请求: 秒杀活动id 1000,商品数量10,执行20次购买操作,使用sychronized同步锁执行,例如:

    Example 1. 使用sychronized同步锁执行
    curl -X POST "http://www.goodskill.com:8080/sychronized" \
    -H "accept: */*" -H "Content-Type: application/json" -d \
    "{ \"requestCount\": 20, \"seckillCount\": 10, \"seckillId\": 1000}"
    Example 2. 使用Redisson分布式锁执行
    curl -X POST "http://www.goodskill.com:8080/redisson" \
    -H "accept: */*" -H "Content-Type: application/json" -d \
    "{ \"requestCount\": 20, \"seckillCount\": 10, \"seckillId\": 1000}"
    Example 3. 使用Redisson分布式锁执行,支持动态配置后台线程池核心线程数以及最大线程数
    curl --location --request POST 'http://www.goodskill.com:8080/limit' \
    --header 'User-Agent: apifox/1.0.0 (https://www.apifox.cn)' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "maxPoolSize": 10,
        "seckillId": 1000,
        "corePoolSize": 2,
        "seckillCount": 100,
        "requestCount": 120
    }'

    请求默认异步执行,可在控制台查看执行日志,如果最终成功交易笔数等于商品数量10则表示没有出现超卖或者少卖问题

🕹️️ 启动完整项目步骤

在快速开始部分基础上增加以下步骤即可启动一个完整项目

  • 进入goodskill-gateway模块,通过GatewayApplication类main方法启动服务网关

  • 找到AuthApplication类main方法启动远程服务

额外功能(可选)

  • 已集成Sentinel限流组件,支持nacos配置中心方式推送限流规则,使用时需启动Sentinel控制台,并以18088端口启动,docker环境暂不支持。

  • Seata分布式事务测试方法见 Seata分布式事务测试示例运行说明

  • main方法运行GoodskillAdminApplication类(微服务健康状态指标监控)

❓常见问题

  1. docker es镜像启动失败

    出现此问题一般为linux环境,运行以下命令即可 sysctl -w vm.max_map_count=262144 ,或者修改/etc/sysctl.conf文件,追加以下配置:

    grep vm.max_map_count /etc/sysctl.conf
    vm.max_map_count=262144
  2. 如何使用本项目自定义的OAuth2.0授权服务器进行登录授权?

    待完善。。

  3. 项目集成的各个框架之间目前的兼容性如何,可以参考本项目的配置在生产环境使用吗?

    本项目目前依赖的各个主流框架的版本比较新,尚未经过完整测试,目前仅用于学习。如果要在生产环境使用,建议使用官方推荐的稳定版本。比如目前的Spring Cloud Alibaba Dubbo官方不建议生产上使用,Spring Cloud Alibaba官方推荐的稳定版为2.2.x[3]

  4. 服务启动控制台报ERROR日志 no available service found in cluster 'default', please make sure registry config correct and keep your seata server running 如何解决?

    启动seata-server服务即可(docker-compose.yml文件中已提供),可参照Seata官网添加nacos相关配置。如未使用分布式事务,可忽略该错误,不影响服务正常运行

  5. docker-compose无法拉取镜像

    hub.docker被墙,国内可使用阿里云镜像加速器,具体操作见 阿里云镜像加速器

  6. 使用JDK17启动项目失败

    启动时添加以下jvm参数,例如:

    --add-opens java.base/java.lang=ALL-UNNAMED
    --add-opens java.base/java.util=ALL-UNNAMED
    --add-opens java.base/java.util.concurrent=ALL-UNNAMED
    --add-opens java.base/java.math=ALL-UNNAMED
    --add-opens java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED

🖲️ 状态机说明

目前秒杀活动状态的控制基于Spring Statemachine状态机实现,使用状态机的优点:

  • 统一控制活动状态,便于状态的集中维护;

  • 防止业务状态被随意更改,保证状态的可控更新;

状态机流程图

image

📚分库分表情况说明

数据库 是否分库 分库字段 是否分表 分表字段

success_killed

MySQL

是(同一服务器中,分为seckill和seckill_01两个库)

seckill_id

是(分为success_killed_0,success_kill_1两张表)

user_phone

📢
其他表均未分库分表,默认使用seckill作为主库

🔖服务网关说明

API接口说明

image

💻相关页面展示

模拟秒杀接口测试

image

📑数据库表结构

image

📖参考文档


1. 由于SpringCloudAlibaba官方暂未支持Dubbo 3.x,本项目采用dubbo-spring-boot-starter集成

About

🐉基于SpringCloud 2022.x +Dubbo 3.x构建的模拟秒杀微服务项目,集成了Elasticsearch🔍、Gateway、Mybatis-Plus、Sharding-JDBC等常用开源组件

License:MIT License


Languages

Language:Java 97.8%Language:Kotlin 1.9%Language:Dockerfile 0.4%