Некоторое время назад я использовал jwt для реализации проверки кода подтверждения электронной почты, аутентификации и входа пользователя.Я также написал специальныйстатьяв заключении.
В той статье упоминался пункт, как ограничить скорость.
В коде подтверждения SMS и коде подтверждения электронной почты, если скорость не ограничена, большое количество QPS будет вызвано вредоносными атаками, которые не только затянут услугу, но и огорчатся тарифом. Ввиду принципа, что джентльмен беден, увеличьте ограничение скорости в моем почтовом сервисе.
Что касается того, как ускорить, есть два сравнительных алгоритма, алгоритмы сливных бар и алгоритмы токена барреля, здесь простое введение, и, наконец, практика в API, я отправил письмо
Ниже приведен API для отправки электронных писем, который был ограничен до двух раз в минуту, вы можете изменить его,email
экспериментировать. вы также можетемой сайтпрямой тест
curl 'https://graphql.xiange.tech/graphql' -H 'Content-Type: application/json' --data-binary '{"query":"mutation SEND($email: String!) {\n sendEmailVerifyCode (email: $email)\n}","variables":{"email":"xxxxxx@qq.com"}}'
Вот моя серия о методах входа в систему
- [Войдите в эти вещи] Реализуйте стиль входа в Material Design.
- [Вход в систему] Используйте jwt, чтобы войти в систему и подтвердить код подтверждения.
- [Вход в систему] Отправка почты, текущий лимит, дырявое ведро и ведро с токенами
Адрес этой статьи:Гора.специальность/пост/рейтинг…
Дырявое ведро
Алгоритм дырявого ведра заключается в том, что капли воды (запросы) сначала попадают в дырявое ведро, а дырявое ведро (ведро) выбрасывает воду с определенной скоростью.
- Ведите счетчик как ведро, верхний предел счетчика - размер ведра
- Отклонить запрос, когда счетчик заполнен
- Периодически очищайте счетчик
использоватьoption
Представителиoption.window
в течение окна время может пройти доoption.max
Запросы
Ниже приведен псевдокод для реализации ограничения тока с использованием счетчиков Redis.
const option = {
max: 10, // window 时间内限速10个请求
window: 1000 // 1s
}
function access(req) {
// 根据请求生成唯一标志
const key = identity(req)
// 计数器自增
const counter = redis.incr(key)
if (counter === 1) {
// 如果是当前时间窗口的第一个请求,设置过期时间
redis.expire(key, window)
}
if (counter > option.window) {
return false
}
return true
}
Вот официальный документ Redis, использующий INCR для реализации текущего ограниченияRedis.io/commands/in...
В это время возникает проблема, которая не проблема, то есть его временное окно не похоже на раздвижное окно. Когда мяч выходит из ведра, может войти еще один мяч. Скорее, это больше похоже на фиксированное время окна времени, где куча шаров выходит из ведра и начнет забить. Из-за этого он может быть получен во второй половине фиксированного окнаmax-1
запрос времени и повторный вызов в следующем фиксированном окнеmax
запросы, самое большее в случайное время окна2 * max - 1
Запросы.
Так же есть редисINCR
иEXPIRE
проблема атомарности, которую легко вызватьRace Condition
, способен пройтиSETNX
решать
redis.set(key, 0, 'EX', option.window, 'NX')
Кроме того, черезLUA
скрипт, чтобы сделать это, очевидно, до сих порSETNX
проще
local current
current = redis.call("incr",KEYS[1])
if tonumber(current) == 1 then
redis.call("expire",KEYS[1],1)
end
Чтобы решить проблему 2N, его можно изменить, чтобы поддерживать очередь вместо обслуживания счетчика. Цена в том, что объем памяти слишком велик, и его сложнее решить.Race Condition
Ниже приведены текущий лимит, реализованный с использованием Set / Get Redis
const option = {
max: 10, // window 时间内限速10个请求
window: 1000 // 1s
}
function access(req) {
// 根据请求生成唯一标志
const key = identity(req)
const current = Date.now()
// cache 视为缓存对象
// 筛选出当前时间窗口的请求个数,每个请求标志为时间戳的格式
// 为了简单这里不做 json 的序列化和反序列化了...
const timestamps = [current].concat(redis.get('timestamps')).filter(ts => ts + option.window > current)
if (timestamps.length > option.max) {
return false
}
// 此时读写不同步,会有 Race Condition 问题
redis.set('timestamps', timestamps, 'EX', option.window)
return true
}
Вот еще один скрипт Lua для решенияRace Condition
Проблема
TODO
Token Bucket (алгоритм Token Bucket)
Посмотрите на разницу между ведром для токенов и дырявым ведром на рисунке.
- Начальное состояние ведра маркеров — это то, что ведро заполнено, а начальное состояние дырявого ведра — то, что ведро пусто.
- Ведро маркеров отклоняет новые запросы, когда оно пусто, а дырявое ведро отклоняет новые запросы, когда ведро заполнено.
- Когда приходит запрос, предположим, что запрос потребляет один токен, ведро ведра токена уменьшается одним токеном, а утечка ведра увеличивается на один токен
Используйте следующее ведро токенов Redis.
TODO