业务场景
双11来了,漫天飞的优惠券怎么实现?
于是我们有了这样一个需求:某商家发优惠券,扣门,就发 10 张,参与的用户假设就 1000 人,假设有 50 人同时领取,那如何保证这个并发量下不会超领取呢?
实现方式
使用Redis
计数器,这只是其应用场景之一,其中将用到三个命令:
exists
:判断传入的key是否存在setnx
:设置值incr
:实现计数功能
手撸代码
代码不是很多,建议自己动手实践下,有注释
这里用到的是ioredis
,地址github.com/luin/ioredi…,可以进去瞅一瞅
新建个文件夹就叫ioredis
吧,然后里面新增两个JS
文件,分别为luck.js
和app.js
,上代码
// luck.js
// 引入Redis
// 这里是要先npm安装的 => npm i ioredis
const Redis = require('ioredis')
const redis = new Redis(6379, '127.0.0.1')
// 将日志写入指定文件,也就是抽中券的和没抽中券的请求一个记录
const fs = require('fs')
const { Console } = require('console')
const output = fs.createWriteStream('./stdout.log') // 抽中券的到这里来
const errorOutput = fs.createWriteStream('./stderr.log') // 没抽中券的到这里来
const logger = new Console(output, errorOutput)
async function luck() {
const count = 10
const key = 'counter:luck'
const keyExists = await redis.exists(key) // 判断key是否存在
// key不存在则初始化设置
if(!keyExists) {
await redis.setnx(key, 0)
}
// 每发送一次领取请求,采用 incr 命令进行自增,由于 Redis 单线程的原因,可以保证原子性,不会出现超领。
const result = await redis.incr(key)
console.log(`result: ${result}`)
if(result > count) { //领取超限
logger.error('luck failure', result)
return
}
logger.info('luck success', result)
}
module.exports = luck
然后app.js
/* 起一个简单的服务,使得浏览器可以访问127.0.0.1:8000/luck接口,
代替一个领取优惠券的操作
*/
const luck = require('./luck')
const http = require('http')
const url = require('url')
const qs = require('querystring')
// 这个花里胡哨的DATA完全可以不要的,因为我们只需要起一个简单的服务
const DATA = userId => ({
code: 0,
success: true,
data: {
userId,
name: '2oops',
descripttion: 'go ahead',
date: new Date()
}
})
// 其实这里是一个比较标准的请求格式
http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
const reqUrl = url.parse(req.url);
if(reqUrl.pathname === '/luck') {
const uid = qs.parse(reqUrl.query).userId;
const RESULT = JSON.stringify(DATA(uid));
luck() // 主要在这里调用就阔以,其他的都是耍流氓
res.end(RESULT)
} else {
res.writeHead(404);
res.end("Not Found")
}
}).listen(8000, () => {
console.log('listening at port: 8000')
})
接下来根目录下运行node app
,这时候你应该会收到这样一个报错,是说没有起Redis server
服务
这里给出问题的解决方法,点这里,再附上windows
环境启动不了redis server
服务的解决方法,亲测有效,windows下redis服务的安装
接下来,我们再运行app.js
,可以在浏览器看到
而且当我们访问这个地址的时候,incr
命令已经执行了一次计数操作
并发压测
最后一步,使用apchebench
做一个并发压力测试,安装教程放这里,(其实这里遇见了一个注册服务出现(OS 5)拒绝访问的问题),按照教程配置好httpd.conf
文件后,bin
目录下运行
.\httpd.exe -k install
.\httpd.exe -k start
ab -c 50 -n 100 http://127.0.0.1:8000/luck
(这里放只100个请求数)
最后我们会看到这个
最终可以看到stdout.log
和stderr.log
文件下分别写入了10个抽中券的记录和40个未抽中券的记录。
总结
Redis
的应用场景很多,这里的计数器只是一种,除了缓存用的比较多之外,还可以实现消息队列,Session存储,发布订阅等。
参考链接