NodeJS向第一个调用API的人插入凭证代码

ljsrvy3e  于 2022-11-22  发布在  Node.js
关注(0)|答案(1)|浏览(186)

我不知道这个问题是否已经有了解决方案,但我找不到它,或者我不知道要搜索什么。
我有一个返回产品列表的rest api,我想在第一个调用api的人的响应中添加一个优惠券代码,我使用redis来缓存收到代码的用户的信息,该代码在15分钟内过期。

async function addVoucherCode(response, userId) {
    try {
    const key = "KEY_VOUCHER_CODE";

    let cachedData = await redis.get(key);
    if (cachedData) {
        if (cachedData.userId === userId) response.voucherCode = cachedData.voucherCode;

        return;
    }

    const voucherCode = await createVoucherCode(userId); //call to create voucher code and save to db
    if (!voucherCode) return;

    await redis.setEx(key, 15 * 60, {userId, voucherCode});
    response.voucherCode = cachedData.voucherCode;

    } catch (err) {
       console.error("[Error] addVoucherCode: ", err);
    }
}

我创建了一个函数来模拟一个同时请求,当我检查响应时,所有的请求都有一个优惠券代码,而不仅仅是第一个。

async function getProducts(url, params) {
try {
    const customers = [
        { id: 1, token: "Bearer eyJhbGciOi....1" },
        { id: 2, token: "Bearer eyJhbGciOi....2"}, 
        { id: 3, token: "Bearer eyJhbGciOi....3"}, 
        { id: 4, token: "Bearer eyJhbGciOi....4"}
    ];

    const data = await Promise.all(customers.map( async customer  => {
        return await fetch(url + "?" + params.toString(), {
            headers: {
                Authorization: customer.token
            },
        }).then(res => res.json());
    }));

    data.forEach((item, indx) => {
       if(item.voucherCode) {
          const id = customers[indx].id;
          console.log(`Customer ${id} has a voucher!!!!!!!!!!!!!`)
       }
    })
} catch (err) {
    console.error("[Error] getProducts: ", err);
}
}

测试结果

Customer 1 has a voucher!!!!!!!!!!!!!
Customer 2 has a voucher!!!!!!!!!!!!!
Customer 3 has a voucher!!!!!!!!!!!!!
Customer 4 has a voucher!!!!!!!!!!!!!

我试着在addVoucherCode中添加一个200ms的延迟,但是结果是一样的。提前感谢你的帮助。

zyfwsgd6

zyfwsgd61#

您在同步循环中调用addVoucherCode,因此它将并行运行4次(4个GET命令将同时发出,它将对所有命令返回null,所有命令都将调用createVoucherCode)。
有两件事可以做来修复它:
1.缓存承诺createVoucherCode

const createVoucherCodePromises = new Map();
function createVoucherCode(userId) {
  if (!createVoucherCodePromises.has(userId)) {
    createVoucherCodePromises.set(
      userId,
      _createVoucherCode(userId)
        .finally(() => createVoucherCodePromises.delete(userId))
    );
  }

  return createVoucherCodePromises.get(userId);
}

async function _createVoucherCode(userId) {
 // ...
}

注意:如果同时运行多个节点进程,则此操作无法解决问题。
1.将SETNX(不会覆盖现有值)和GET(返回现有/旧值)一起使用

> SET key voucher1 NX GET
OK
> SET key voucher2 NX GET # will return the existing value without overriding it
"voucher1"
> GET key
"voucher1"

相关问题