實戰:前端如何使用Redis實現一個雙11的領券功能

業務場景

雙11來了,漫天飛的優惠券怎麼實現?javascript

因而咱們有了這樣一個需求:某商家發優惠券,扣門,就發 10 張,參與的用戶假設就 1000 人,假設有 50 人同時領取,那如何保證這個併發量下不會超領取呢?html


實現方式

使用Redis計數器,這只是其應用場景之一,其中將用到三個命令:java

  • exists:判斷傳入的key是否存在
  • setnx:設置值
  • incr:實現計數功能

手擼代碼

代碼不是不少,建議本身動手實踐下,有註釋node

這裏用到的是ioredis,地址github.com/luin/ioredi…,能夠進去瞅一瞅laravel

新建個文件夾就叫ioredis吧,而後裏面新增兩個JS文件,分別爲luck.jsapp.js,上代碼git

// 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.jsgithub

/* 起一個簡單的服務,使得瀏覽器能夠訪問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服務redis

這裏給出問題的解決方法,點這裏,再附上windows環境啓動不了redis server服務的解決方法,親測有效,windows下redis服務的安裝npm

接下來,咱們再運行app.js,能夠在瀏覽器看到json

並且當咱們訪問這個地址的時候,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.logstderr.log文件下分別寫入了10個抽中券的記錄和40個未抽中券的記錄。


總結

Redis的應用場景不少,這裏的計數器只是一種,除了緩存用的比較多以外,還能夠實現消息隊列,Session存儲,發佈訂閱等。

參考連接

相關文章
相關標籤/搜索