Redis 应用场景分析:Session 共享、分布式锁、秒杀功能等

Redis 是一个高性能的键值数据库,也是一个非常流行的用于缓存、持久化和消息队列的解决方案。在前端领域,Redis 有许多应用场景,例如 Session 共享、分布式锁、秒杀等功能。本文将深入探讨这些应用场景,并提供示例代码和指导意义。

Session 共享

在 Web 应用程序中,Session 是一种存储用户信息的机制。当用户访问一个 Web 应用程序时,服务器将创建一个唯一的 Session,并将其与此用户的浏览器相关联。Session 可以存储用户的身份验证信息、购物车内容等,使 Web 应用程序能够跨页面保持用户状态。

使用 Redis 实现 Session 共享是一个常见的做法,因为 Redis 具有高可用性和可扩展性。在 Redis 中,将 session 数据存储在内存中可以大大提高访问速度。同时,Redis 的数据复制和持久化机制可以确保 Session 数据的可靠性和安全性。

下面是示例代码:

const redis = require("redis");
const express = require("express");
const session = require("express-session");
const RedisStore = require("connect-redis")(session);

const redisClient = redis.createClient();

const app = express();

app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: "my-secret-session-key",
  resave: false,
  saveUninitialized: false
}));

app.get("/", (req, res) => {
  if (req.session.views) {
    req.session.views++;
    res.send(`You have visited this page ${req.session.views} times.`);
  } else {
    req.session.views = 1;
    res.send("Welcome to this page for the first time!");
  }
});

上述代码使用 Express.js 和 Redis 创建了一个简单的 Web 应用程序,使用 Redis 存储 Session 数据,并在用户访问页面时使用 Session 计数器跟踪访问次数。

分布式锁

在分布式系统中,为了保证数据的一致性,必须对某些操作进行串行化处理,以保证同一时刻只有一个节点能够执行该操作。分布式锁是一种解决方案,它可以确保只有一个节点能够持有锁,并且在该节点释放锁之前,其他节点不能获得锁。

使用 Redis 实现分布式锁时,可以将锁作为 Redis 的 Key,将节点的 ID 作为 Value,使用 Redis 的 setnx 命令尝试将 Key 设置为 1,只有设置成功的节点才能持有锁。

下面是示例代码:

const redis = require("redis");

class DistributedLock {
  constructor(redisClient, key, nodeId, timeout = 30000) {
    this.redisClient = redisClient;
    this.key = key;
    this.nodeId = nodeId;
    this.timeout = timeout;
  }

  async acquire() {
    const start = Date.now();
    while (true) {
      const result = await new Promise((resolve, reject) => {
        this.redisClient.set(this.key, this.nodeId, "PX", this.timeout, "NX", (err, result) => {
          if (err) reject(err);
          resolve(result);
        });
      });
      if (result === "OK") return true;
      if ((Date.now() - start) > this.timeout) return false;
      await new Promise(resolve => setTimeout(resolve, 100));
    }
  }

  release() {
    this.redisClient.del(this.key);
  }
}

const redisClient = redis.createClient();
const lock = new DistributedLock(redisClient, "my-lock", "node-1");

async function doSomethingInLock() {
  const acquired = await lock.acquire();
  if (acquired) {
    try {
      // do something that requires lock
    } finally {
      lock.release();
    }
  } else {
    throw new Error("Failed to acquire lock in time");
  }
}

上述代码演示了如何使用 Redis 实现分布式锁。DistributedLock 类接受 Redis 客户端、锁的 Key、节点 ID 和过期时间等参数,定义了 acquire 和 release 方法用于获取和释放锁。在 doSomethingInLock 函数中,调用 lock.acquire 尝试获取锁,并在获取到锁后执行需要保护的操作。

秒杀功能

秒杀是一种特殊的电子商务模式,具有高并发性和固定时间窗口的特点。在秒杀活动中,商品数量有限,需要在一定的时间内完成大量的购买请求,并保证每个请求的唯一性。使用 Redis 可以很好地解决秒杀活动中的高并发和幂等性问题。

下面是示例代码:

const redis = require("redis");

async function processOrder(orderId) {
  const redisClient = redis.createClient();
  const orderKey = `order-${orderId}`;
  const boughtKey = `bought-${orderId}`;
  const isExist = await new Promise((resolve, reject) => {
    redisClient.hexists(boughtKey, orderKey, (err, result) => {
      if (err) reject(err);
      resolve(result);
    });
  });
  if (isExist) return; // this order has been processed
  const availableCount = await new Promise((resolve, reject) => {
    redisClient.llen("product-storage", (err, result) => {
      if (err) reject(err);
      resolve(result);
    })
  });
  if (availableCount === 0) return; // no product available
  redisClient.watch("product-storage");
  const tx = redisClient.multi();
  tx.rpop("product-storage");
  tx.hset(boughtKey, orderKey, new Date().toISOString());
  const results = await new Promise((resolve, reject) => {
    tx.exec((err, results) => {
      if (err) reject(err);
      resolve(results);
    })
  });
  if (results === null) return; // transaction failed due to race condition
  // send a confirmation email to the user
}

上述代码演示了如何使用 Redis 解决秒杀活动中的高并发和幂等性问题。在 processOrder 函数中,首先检查订单是否已经处理过,如果已经处理过则直接返回。然后获取商品数量,如果商品数量为 0 则直接返回。接着调用 Redis 的 watch 函数对商品数量进行监控,并开启一个事务,将商品数量减 1,并将订单标记为已购买。最后提交事务,如果事务失败则说明商品数量已经改变,返回即可。如果提交成功,则说明订单已经处理成功,可以发送一封确认邮件给用户。

总结

Redis 是一个功能强大的键值数据库,应用广泛。本文深入探讨了 Redis 在前端领域的三个常见应用场景:Session 共享、分布式锁、秒杀。使用 Redis 可以大大提高应用程序的可靠性、可扩展性和性能。希望通过本文的介绍和示例代码,读者可以更好地理解 Redis 和其应用场景,并在实际开发中灵活应用。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65a768f2add4f0e0ff0786e6


纠错反馈