Redis-聊天室和秒杀

366 阅读2分钟

Redis

基于内存的键值数据结构,用于数据库,缓存,消息代理。

1.特点

  1. 保存在内存速度快
  2. Redis支持持久化,断电不丢数据,对数据的更新异步保存到磁盘上
  3. 支持类型多
  4. 支持事务,所有操作都是原子性。
  5. 场景丰富,(1)消息处理,按key设置过期事件,(2)过期自动删除

2.场景

  1. 缓存
  2. 分布式锁:支持多台服务器横向部署,共用一份数据
  3. 自动过期
  4. 计数器和排行榜
  5. 秒杀的缓冲层
  6. 处理签到和其他状态(大数据处理)
  7. 发布/订阅

3.例子

1.聊天室实现

#注意该代码只使用的订阅发布,不能实现多服务器部署
const Koa = require('koa');
const app = new Koa();
const Router = require('koa-router');
const fs = require('fs');
const server = require('http').createServer(app.callback());
const io = require('socket.io')(server);
const redis = require('redis')

// 首页路由
const router = new Router();
router.get('/', ctx => {
    ctx.response.type = 'html';
    ctx.response.body = fs.createReadStream('./index.html');
});
app.use(router.routes());

const rclient = redis.createClient(6379, '127.0.0.1')
rclient.on("ready", err => {
    console.log('client ready ....');
})

const publish = redis.createClient(6379, 'localhost')
publish.on('ready', err => {
    console.log('publish ready ...')
})

// socket连接
io.on('connection', (socket) => {
    // redis模式
    // 订阅
    rclient.subscribe('chat')
    // 收到redis的发布,开始处理消息给客户端
    rclient.on('message', (channel, msg) => {
        io.emit('chat message', msg)//通过socket发送消息给客户端
    })
    socket.on('chat message', msg => {
        console.log('receive message:' + msg)
        publish.publish('chat', msg)//这里通过redis发布消息
    })
    socket.on('disconnect', () => {
        console.log('user disconnected');
    })
});

// 监听端口
server.listen(3000, () => {
    console.log('listening on *:3000');
});

测试地址:

2.实现秒杀

index.js

//该代码可以用于多服务器部署
const Koa = require("koa");
const app = new Koa();
const Router = require("koa-router");
const redis = require("redis");
const redisClient = redis.createClient(6379, "localhost");

const wrapper = require("co-redis");
const client = wrapper(redisClient);

client.on("ready", () => {
  console.log("redis ready ...");
});

// 首页路由
const router = new Router();
router.get("/", (ctx) => {
  ctx.response.type = "html";
  ctx.response.body = fs.createReadStream("./index.html");
});

router.get("/create", async (ctx) => {

  // 清空商品
  await client.ltrim("goods", -1, 0);

  // 添加30个商品
  new Array(30).fill().forEach(async (v, i) => {
    await client.rpush("goods", i);
    console.log("添加商品:", i);
  });

  // redis llen
  const num = await client.llen("goods");
  console.log("抢购商品数量:", num);

  ctx.body = {
    ok: 1,
  };
});

/**
 * 秒杀
 */
router.get("/buy", async (ctx) => {
  // 产生一个随机数当做用户id
  const uid = (Math.random() * 9999999).toFixed();
  let pid = await client.lpop("goods");

  // 判断库存
  if (pid) {
    await client.hset("orders", pid, uid);
    console.log("订单生成", pid, uid);
  }else {
    console.log('已抢光')
  }

  ctx.body = { ok: 1 };
});

router.get("/order", async (ctx) => {
  const keys = await client.hkeys("orders");
  console.log('订单列表')
  console.log('===========')
  const orders = await client.hgetall('orders')
  for (k of keys) {
    console.log(`${k} => ${await client.hget("orders", k)}`);
  }

  ctx.body = {
    orders
  };
});

router.get('/order/clear',async ctx => {
  const keys = await client.hkeys("orders");
  for (k of keys) {
    console.log(`删除订单: ${k} => ${await client.hdel("orders", k)}`);
  }
  ctx.body = {ok : 1}
})

app.use(router.routes());

// 监听端口
app.listen(3000, () => {
  console.log("listening on *:3000");
});

// node index.js //启动服务

test.js 测试代码 ,使用autocannon创建100个buy请求

(async () => {
  const autocannon = require("autocannon");
  const result = await autocannon({
    url: "http://localhost:3000/buy",
    connections: 100, //default
    pipelining: 1, // default
    duration: 1, // default
  });
  console.log('秒杀完成');
})()
// node test.js //启动测试代码

测试地址: