在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
秒杀活动可以说在互联网上随处可见,从12306抢票,到聚划算抢购,我们生活的方方面面都可以看到秒杀的身影。秒杀的架构设计也是对于一个架构师架构设计能力的一次考验。本文的目的并不在于提供一个可以直接落地的设计方案,而是意在提供一个简单的方法,一个思路,使大家能够对于秒杀背后的一些设计有更感性的认识, 并且可以自己亲自动手实践一下。所有的配置及源码都在本文最后的GitHub repository中可以找到。 首先,先简单介绍下本文中会涉及到的一些组件,如下图所示:
JMeter:用JMeter来模拟秒杀活动中大量并发的用户请求 Seckill Service:基于Nodejs使用Express实现的秒杀service,图中的步骤2,3,4都是在这个service中处理的 Redis:一个Redis的docker container,在其中保存一个名为counter的数据来表示当前剩余的库存大小 Kafka: 一个Kafka的docker container,其实这里还有一个zookeeper的docker container,Kafka用zookeeper来存放一些元数据,在程序中并没有涉及到,所以也就不单独列出来说了。Seckill service在更新完Redis之后,会发送一条消息给Kafka表示一次成功的秒杀 Seckill Kafka Consumer: 基于Nodejs的Kafka consumer,会从Kafka中去获取秒杀成功的消息,处理并且存储到MySQL中 MySQL:一个MySQL的docker container,最终秒杀成功的请求都会对应着数据库表中的一条记录 环境搭建 1 . 安装JMeter
在这个Thread Group下新建一个Http Request的Sampler并命名为Seckill,按下图配置host name,port number,http request method以及request path
2 . 安装Redis,Kafka, Zookeeper和MySQL 3 . 编写Docker Compose文件
配置文件中一共配置了4个services对应4个docker container,分别是zookeeper,kafka,redis以及mysql。这里有两个地方需要设置成你实际环境的值,一个是kafka配置下面的KAFKA_ADVERTISED_HOST_NAME字段,这个需要设置成本地机器的IP。另一个是MYSQL配置下面的MYSQL_ROOT_PASSWORD,你可以设置成你想要的任何值。 创建好这个文件之后,就可以去命令行项目根目录中执行docker-compose up,docker engine就会把上面配置的这4个组件全部启动起来。 注意:在启动完之后,需要去Kafka容器中创建一个名为CAR_NUMBER的topic,去Redis容器中创建一个名为counter的计数器(设置值为100,代表库存初始值为100),去MySQL容器中创建一个名为seckill的数据表(包含一个自增长的id自段和一个timestamp格式的date字段)。 代码片段 1 . Seckill Service
第1-8行,引入了程序需要用到的对象,nodejs的mvc框架express, redis, kafka等 第10行,利用express提供的方法暴露出一个path为/seckill的POST方法 第12行,定义了一个方法,在54行会调用 第13-22行,新建了一个redis client并且监听error事件 第23行,这行代码非常关键,它的作用是让redis cilent监视redis中的counter值,之后会启动一个事务,如果在事务提交的时候发现有其它client修改了counter值的话,就会放弃这个事务。 第24行,通过redis client的异步方法获取counter的值,因为redis的get操作是原子的,所以在这里不用担心有并发读写的问题。 第25-28行,判断返回的库存值是否大于0,如果大于0,通过client.multi()启动一个事务,通过decr()方法将counter值减1,最后通过exec()方法提交事务;如果小于0,则执行第47行,打印卖完了并且关闭redis client。 第29-46行,这里我们看一下multi.exec()中的这个回调方法。在前面我们已经使用watch对counter进行了监视。如果在事务提交过程中有其它client修改了counter值的话,回调方法中的replies参数就会是null,可以看到第29-31行,程序会打印“可能有冲突”并且再次调用fn方法重试。 2 . seckill_kafka_consumer
这里的代码就比较简单了,会初始化一个kafka consumer监听CAR_NUMBER topic,对于新获取的消息会去MySQL的seckill表里插入一条记录。 操作步骤 启动docker container 启动Seckill_Service 启动seckill_kafka_consumer 启动JMeter发送2000个并发请求 结果 JMeter request results Redis counter field
MySQL seckill table
可以看到,最后redis中的counter变成0,seckill数据表中会插入100条记录,没有发生超卖或者少卖的情况。当然在实际生产环境场景中,还有许多其它需要考虑的地方,希望此文可以起到一个抛砖引玉的作用,帮助大家更好的理解秒杀场景。 项目GitHub地址: MockSecKill |
请发表评论