Серия статей
- Приложение Redis-Распределенная блокировка
- Приложение Redis – асинхронная очередь сообщений и очередь задержки
- Приложение Redis-Растровое изображение
- Приложение Redis-HyperLogLog
- Приложение Redis-Фильтр цветения
- Приложение Redis-Ограничение по Току
- Приложение Redis-Geo
В сценариях с высоким уровнем параллелизма существуют три системы защиты от острого оружия: кэширование, деградация и ограничение тока. Цель кэширования состоит в том, чтобы ускорить доступ к системе и увеличить пропускную способность, которую может обрабатывать система; деградация временно блокируется, когда служба выходит из строя или влияет на производительность основного процесса. В некоторых сценариях необходимо ограничить количество одновременных запросов, таких как повторное уничтожение, оснастка, публикация, комментарии, вредоносные сканеры и т.д.
Алгоритмы ограничения тока Распространенными алгоритмами ограничения тока являются счетчик, дырявое ведро и ведро токенов.
Счетчик
Как следует из названия, это вопрос запоминания одного за другим, а затем принятия решения о том, превышает ли число в ограниченном временном интервале лимит.
function isActionAllowed($userId, $action, $period, $maxCount)
{
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$key = sprintf('hist:%s:%s', $userId, $action);
$now = msectime (); millisecond timestamp
$pipe = $redis - > multi (Redis:: PIPELINE); // Pipeline for performance improvement
$pipe - > zadd ($key, $now, $now); both // value and score use millisecond timestamps
$pipe - > zremrange by score ($key, 0, $now - $period); // Remove the behavior record before the time window, and the rest is in the time window
$pipe - > zcard ($key); // Get the number of actions in the window
$pipe - > expire ($key, $period + 1); // plus one second expiration time
$replies = $pipe->exec();
return $replies[2] <= $maxCount;
}
for ($i=0; $i<20; $i++){
Var_dump (isAction Allowed ("110", "reply", 60*1000, 5)); // Execution can find that only the first five passes.
}
// Returns the current millisecond timestamp
function msectime() {
list($msec, $sec) = explode(' ', microtime());
$msectime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
return $msectime;
}Дырявое ведро
Алгоритм дырявого ведра имеет простую идею. Вода (по запросу) сначала поступает в дырявое ведро. Из дырявого ведра вытекает вода с определенной скоростью (интерфейс имеет скорость отклика). Когда вода поступает слишком быстро, она переливается напрямую (частота доступа превышает скорость отклика интерфейса). Затем он отклоняет запрос. Можно видеть, что алгоритм “дырявого ведра” может принудительно ограничить скорость передачи данных. Следующим образом:
Конкретный код реализуется следующим образом
Основная логика-освободить место, которое вызывается перед каждым орошением, чтобы вызвать утечку и освободить место для воронки. Воронки могут использовать хэш-структуру в Redis для хранения соответствующих полей. При орошении мы можем извлечь поля для логической операции, а затем сохранить их в хэш-структуре для завершения определения частоты. Но проблема в том, что атомарность всего процесса не может быть гарантирована, что означает использование блокировок для управления, но в случае сбоя блокировки ее необходимо повторить или отменить, что приведет к снижению производительности и повлияет на пользовательский интерфейс, а сложность кода также увеличится. В это время Redis предоставляет плагин, появляется Redis-ячейка.
Повторная ячейка
Redis 4.0 предоставляет модуль Redis с ограничением тока, называемый redis-ячейкой, который предоставляет алгоритм воронки и инструкции по ограничению атомарного тока.
Модуль имеет только одну инструкцию cl. дроссельная заслонка, ее параметры и возвращаемые значения сложны.
> cl.throttle tom:reply 14 30 60 1 1) Integer 0# 0 denotes permission and 1 denies refusal. 2) Integer 15 # funnel capacity 3) Integer 14 # funnel remaining space left_quota 4) Integer - 1 # If rejected, how long will it take to retry in unit seconds? 5) How long after integer 2, the funnel is completely empty, in unit seconds
Эта инструкция означает, что поведение пользователя тома в ответ может происходить не более 30 раз за 60 секунд, а начальная емкость воронки составляет 15 (поскольку она составляет от 0 до 14-15), при этом пространство по умолчанию, занимаемое каждым действием, равно 1 (необязательные параметры). Если отклонено, режим ожидания с четвертым значением возвращаемого массива может быть использован в качестве времени повторной попытки или асинхронной задачи синхронизации для повторной попытки.
Ведро для токенов
Ведро с токенами и Дырявое ведро имеют одинаковый эффект, но противоположное направление легче понять. Со временем система добавит токен в корзину с постоянным интервалом 1/QPS (10 мс, если). Если ведро будет полным, то этого не произойдет. Кроме того, когда поступят новые запросы, каждый из них получит токен. Если токена нет, они заблокируют или откажут в обслуживании.
Еще одним преимуществом корзин токенов является то, что они могут легко изменять скорость. Как только необходимо увеличить ставку, количество токенов, размещенных в корзинах, может быть увеличено по требованию. Как правило, определенное количество токенов добавляется в корзину в определенное время (например, 100 миллисекунд). Некоторые варианты рассчитывают количество токенов, которое должно быть увеличено в режиме реального времени.
Конкретная реализация управления ссылочным трафиком PHP на основе redis с использованием алгоритма корзины токенов
_config = $config;
$this->_queue = $queue;
$this->_max = $max;
$this->_redis = $this->connect();
}
/**
* Added token
*@ Number of tokens added by param Int $num
*@ Number of return Int additions
*/
public function add($num = 0)
{
// Current number of remaining tokens
$curnum = intval($this->_redis->lSize($this->_queue));
// Maximum number of tokens
$maxnum = intval($this->_max);
// Calculate the maximum number of tokens that can be added, not exceeding the maximum number of tokens
$num = $maxnum >= $curnum + $num ? $num : $maxnum - $curnum;
// Added token
if ($num > 0) {
$token = array_fill(0, $num, 1);
$this->_redis->lPush($this->_queue, ...$token);
return $num;
}
return 0;
}
/**
* Getting tokens
* @return Boolean
*/
public function get()
{
return $this->_redis->rPop($this->_queue) ? true : false;
}
/**
* Reset the token bucket and fill it with tokens
*/
public function reset()
{
$this->_redis->delete($this->_queue);
$this->add($this->_max);
}
private function connect()
{
try {
$redis = new Redis();
$redis->connect($this->_config['host'], $this->_config['port'], $this->_config['timeout'], $this->_config['reserved'], $this->_config['retry_interval']);
if (empty($this->_config['auth'])) {
$redis->auth($this->_config['auth']);
}
$redis->select($this->_config['index']);
} catch (\RedisException $e) {
throw new Exception($e->getMessage());
return false;
}
return $redis;
}
}
$config = array(
'host' => 'localhost',
'port' => 6379,
'index' => 0,
'auth' => '',
'timeout' => 1,
'reserved' => NULL,
'retry_interval' => 100,
);
// Token Bucket Container
$queue = 'mycontainer';
// Maximum number of tokens
$max = 5;
// Create TrafficShaper objects
$oTrafficShaper = new TrafficShaper($config, $queue, $max);
// Reset the token bucket and fill it with tokens
$oTrafficShaper->reset();
// The last three acquisitions fail because there are only five tokens in the token bucket.
for ($i = 0; $i < 8; $i++) {
var_dump($oTrafficShaper->get());
}
// Add 10 tokens, the maximum token is 5, so only 5 tokens can be added.
$add_num = $oTrafficShaper->add(10);
var_dump($add_num);
// The last acquisition failed because there were only five tokens in the token bucket.
for ($i = 0; $i < 6; $i++) {
var_dump($oTrafficShaper->get());
}
?>Эта статья также опубликована на общедоступном номере Wechat (Информация о следе). Пожалуйста, обратите на это внимание.