如何确保邀请代码在Redis中是唯一的

时间:2018-08-22 12:14:14

标签: javascript node.js redis node-redis

我需要生成一个唯一的用户友好代码,并将其保存到Redis中,直到受邀用户对其评分或过期为止。 由于代码必须是用户友好,因此我决定使用6位数字,前端会将其分为两组,例如xxx-xxx。

现在,在后端,我有NodeJS和node_redis。 这就是我生成随机字符串并将其保存到Redis中的方式:

    var invCode = Math.floor(Math.random() * 90000) + 100000;
    var key = "invitation-code:" + invCode;
    const TTL = 3 * 24 * 60 * 60; // 3 days

    redis.client.existsAsync(key)
        .then(res => {
            if (!res) {
                // ok, I can add the key, value pair
                return redis.client.setAsync(key, value, 'EX', TTL);
            } else {
                // I have to generate new key and check it again
                // how can I re-iterate the process???
                return null;
            }
        })
        .then(res => {
            logger.info('InvitationCodeController::generate added <' + key + ', ' + value + '> pair');
        })
        .catch(error => {
            logger.error('InvitationCodeController::generate Error ' + error);
        });

现在,我无法弄清楚的是-如果生成的代码已经存在,我如何再次重复该过程,即生成另一个随机字符串,对其进行格式化,签入Redis等。 由于我有一个异步调用,因此我认为没有任何一种循环对我有用吗?

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

您可以利用如下的“尝试”过程。 您也可以通过删除--n部分来类似地进行while循环。

此外,我认为您应该对“ SETNX”使用“ NX”参数---当该值不存在时设置。否则,有可能在您检查redis密钥是否存在与实际设置密钥之间,您可能会覆盖其他密钥。您甚至可能会在此时重写它,因此请依靠SETNX在设置失败时抛出错误,而不是每次都检查该值。

const process = require('process');
const redis = require("redis");
const Bluebird = require('bluebird')
Bluebird.promisifyAll(redis.RedisClient.prototype)
Bluebird.promisifyAll(redis.Multi.prototype)
const winston = require('winston');
const logger = winston.createLogger({
  level: 'silly',
  format: winston.format.json(),
  transports: [new winston.transports.Console({
    format: winston.format.simple()
  })]
});

const client = redis.createClient({
  host:'redis-19141.c16.us-east-1-3.ec2.cloud.redislabs.com',
  port:'19141'
});
client.auth('I6C2ISvac4suTbxSYcbsjWiz635NK8Wv');
// client.set("string key", "string val", redis.print);

var invCode = Math.floor(Math.random() * 90000) + 100000;
// test invCode being the same --- retry.
invCode = 111111;
var key = "invitation-code:" + invCode;
const TTL = 3 * 24 * 60 * 60; // 3 days

let value = "test";
const trySet = function(key,n){
  const used = process.memoryUsage().heapUsed / 1024 / 1024;
  logger.info(`The script uses approximately ${Math.round(used * 100) / 100} MB`);
  return client.existsAsync(key)
  .then(res => {
    logger.info("existsAsync res",res);
    if (!res) {
      logger.info("Key does not exist!");
      return client.setAsync(key, value, 'NX','EX', TTL) 
        .then(res => {
          logger.info('InvitationCodeController::generate added <' + key + ', ' + value + '> pair');
          return true;
        })
    } else {
      logger.info("Key already exists!");
      if(n > 0){
        return trySet(key,--n);
      }else{
        return false;
      }
    }
  })
  .catch(error => {
    logger.error('InvitationCodeController::generate Error ' + error);
    return false;
  });
}

trySet(key,50).then(function(res){
  if(res){
    logger.info('trySet::success');
  }else{
    logger.info('trySet::failed');
  }
}).catch(error => {
  logger.error('trySet::error');
});

https://repl.it/repls/ImmediateSufficientCoin

答案 1 :(得分:0)

由于代码生成是同步过程,所以我知道,我可以用其他方法来实现。这是代码:

    const TTL = 3 * 24 * 60 * 60; // 3 days
    var invCode = '';
    const pattern = "invitation-code:";
    var prepKey = '';

    redis.client.keysAsync(pattern + "*")
        .then(keys => {
            // these are all keys / invitation codes
            var isFound = false;
            do {
                invCode = Math.floor(Math.random() * 90000) + 100000;
                prepKey = pattern + invCode;
                // traverse keys to check if the invitation code matches
                keys.forEach(key => {
                    if (key === prepKey) {
                        isFound = true;
                    }
                });
            } while (isFound);
            return prepKey;
        })
        .then(key => {
            return redis.client.setAsync(key, value, 'EX', TTL);
        })
        .then(res => {
            logger.info('InvitationCodeController::generate added <' + prepKey + ', ' + value + '> pair');
        })
        .catch(error => {
            logger.error('InvitationCodeController::generate Error ' + error);
        });

希望这对其他人有帮助...