Ограничить конечную точку API общим количеством запросов каждого пользователя - PullRequest
0 голосов
/ 18 января 2019

В настоящее время я изучаю решения для ограничения доступа к конечной точке API на NodeJS по общему количеству запросов в месяц.

Например, я хочу, чтобы пользователи бесплатного плана получали доступ к конечной точке /api до 100 запросов в месяц, а пользователи премиум-плана получали 5000 запросов в месяц.

Наивный способ обойти это - внедрить промежуточное программное обеспечение для паспорта, чтобы получить план пользователя и затем отслеживать количество:

  app.get("/api", requireAuth, async (req, res, next) => {
    try {
        // Check if user ran out of requests
        if (req.user.apiRequestsLeft === 0) {
          res.send("You ran out of API requests!")
        } else {
          // Decrement the allocated requests
          req.user.apiRequestsLeft--;
          await req.user.save();
          res.send(user)
        }
    } catch (err) {
      next(err);
    }
  });

Мои опасения:

  1. Проблемы производительности / масштабируемости, связанные с необходимостью обновлять документ MongoDB каждый раз при поступлении запроса - возможно ли это, или я столкнусь с проблемой при расширении приложения?
  2. Сброс счетчика - должен ли это быть ежедневный cronjob, который просматривает временную метку «регистрации» каждого пользователя, вычисляет, прошел ли месяц, и соответственно сбрасывает выделенные запросы, или есть лучший способ создать что-то вроде это?

1 Ответ

0 голосов
/ 18 января 2019

Проблемы производительности / масштабируемости при обновлении документа MongoDB каждый раз, когда есть запрос - это возможно или я столкнусь с проблемой когда приложение растет?

Определенно. Вскоре вы столкнетесь с большим трафиком на mongoDB, и это приведет к снижению производительности. На мой взгляд, вы должны использовать более быструю базу данных в памяти, такую ​​как Redis , чтобы справиться с ситуацией. Вы даже можете использовать Redis как session-store, что уменьшит нагрузку на MongoDB. Таким образом, MongoDB можно использовать для других бизнес-запросов.

Сброс счетчика - если это ежедневный хрен, который смотрит на метка времени регистрации каждого пользователя, если вычисляется месяц передал и сбросил распределенные запросы соответственно, или есть лучший способ создать что-то вроде этого?

Лучшим способом было бы добиться сброса в самом промежуточном программном обеспечении.

Вот код, объясняющий мое решение.

Пример проекта Quota объекта будет:

{
    type: "FREE_USER",                  /** or "PREMIUM_USER" */
    access_limit: 100,                  /** or 5000 */
    exhausted_requests: 42              /** How many requests the user has made so far this month */
    last_reset_timestamp: 1547796508728 /** When was the exhausted_requests set to 0 last time */
}

С этим дизайном. Ваше промежуточное ПО, которое проверяет квоту, будет выглядеть примерно так:

const checkQuota = async (req, res, next) => {
    const user = req.user;
    const userQuotaStr = await redis.getAsync(user.id)
    let userQuota;
    /** Check if we have quota information about user */
    if (userQuotaStr != null) {
        /** We have previously saved quota information */
        userQuota = JSON.parse(userQuotaStr);

        /** 
         * Check if we should reset the exhausted_requests
         * Assuming that all the requests are reset on the First Day of each month.
         */
        if ( isStartOfMonth() ) {
            /** 
             * It is First Day of the month. We might need to reset the `exhausted_requests` 
             * Check the difference between `Date.now()` and `userQuota.last_reset_timestamp`
             * to determine whether we should reset or not
             */
            if ( shouldResetTimeStamp(userQuota.last_reset_timestamp) ) {
                userQuota.exhausted_requests = 0
                userQuota.last_reset_timestamp = Date.now()
            }
        }
    } else {
        /** We do not have previously saved quota information. Prepare one */
        userQuota = {
            type: user.type,
            access_limit: user.access_limit,
            exhausted_requests: 0,
            last_reset_timestamp: Date.now()
        }
    }

    /** Incredement the counter to account the current request */
    userQuota.exhausted_requests++

    /** Update in database */
    redis.set(user.id, JSON.stringify(userQuota))


    if ( userQuota.exhausted_requests >= userQuota.access_limit ) {
        /** User has reached the quota limit. Deny the request. set with 401 or 403 status code */
    } else {
        /** User can access the API. call next() */
    }   
}

Конечно, фрагмент является неполным. Это просто дает вам представление о том, как приступить к написанию этого промежуточного программного обеспечения.

Вот как вы можете использовать промежуточное ПО для своих API:

/** If requests to routes are under the quota */
app.get("/api/quota-routes", requireAuth, checkQuota, /** Mount the actual middleware here */)

/** If requests to routes are unlimited, just remove the checkQuota middleware */
app.get("/api/unlimited-routes", requireAuth, /** Mount the actual middleware here */)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...