Implemented WebHooks delivery queue.

Completely removed usage of the RabbitMQ. Queue now based on Redis channels.
Worker process now extracted as separate docker container.
Base image upgraded to the 1.8.0 version (PHP 7.2.7 and pcntl extension).
This commit is contained in:
ErickSkrauch
2018-07-08 18:20:19 +03:00
parent 6751eb6591
commit c0aa78d156
55 changed files with 933 additions and 1684 deletions

View File

@@ -1,13 +1,11 @@
<?php
namespace api\modules\accounts\models;
use api\exceptions\ThisShouldNotHappenException;
use api\modules\internal\helpers\Error as E;
use common\helpers\Amqp;
use common\models\Account;
use common\models\amqp\AccountBanned;
use PhpAmqpLib\Message\AMQPMessage;
use common\tasks\ClearAccountSessions;
use Yii;
use yii\base\ErrorException;
class BanAccountForm extends AccountActionForm {
@@ -38,7 +36,7 @@ class BanAccountForm extends AccountActionForm {
];
}
public function validateAccountActivity() {
public function validateAccountActivity(): void {
if ($this->getAccount()->status === Account::STATUS_BANNED) {
$this->addError('account', E::ACCOUNT_ALREADY_BANNED);
}
@@ -54,27 +52,14 @@ class BanAccountForm extends AccountActionForm {
$account = $this->getAccount();
$account->status = Account::STATUS_BANNED;
if (!$account->save()) {
throw new ErrorException('Cannot ban account');
throw new ThisShouldNotHappenException('Cannot ban account');
}
$this->createTask();
Yii::$app->queue->push(ClearAccountSessions::createFromAccount($account));
$transaction->commit();
return true;
}
public function createTask(): void {
$model = new AccountBanned();
$model->accountId = $this->getAccount()->id;
$model->duration = $this->duration;
$model->message = $this->message;
$message = Amqp::getInstance()->prepareMessage($model, [
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
]);
Amqp::sendToEventsExchange('accounts.account-banned', $message);
}
}

View File

@@ -2,13 +2,10 @@
namespace api\modules\accounts\models;
use api\aop\annotations\CollectModelMetrics;
use api\exceptions\ThisShouldNotHappenException;
use api\validators\EmailActivationKeyValidator;
use common\helpers\Amqp;
use common\models\amqp\EmailChanged;
use common\models\EmailActivation;
use PhpAmqpLib\Message\AMQPMessage;
use Yii;
use yii\base\ErrorException;
class ChangeEmailForm extends AccountActionForm {
@@ -35,30 +32,14 @@ class ChangeEmailForm extends AccountActionForm {
$activation->delete();
$account = $this->getAccount();
$oldEmail = $account->email;
$account->email = $activation->newEmail;
if (!$account->save()) {
throw new ErrorException('Cannot save new account email value');
throw new ThisShouldNotHappenException('Cannot save new account email value');
}
$this->createTask($account->id, $account->email, $oldEmail);
$transaction->commit();
return true;
}
public function createTask(int $accountId, string $newEmail, string $oldEmail): void {
$model = new EmailChanged();
$model->accountId = $accountId;
$model->oldEmail = $oldEmail;
$model->newEmail = $newEmail;
$message = Amqp::getInstance()->prepareMessage($model, [
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
]);
Amqp::sendToEventsExchange('accounts.email-changed', $message);
}
}

View File

@@ -4,13 +4,10 @@ namespace api\modules\accounts\models;
use api\aop\annotations\CollectModelMetrics;
use api\exceptions\ThisShouldNotHappenException;
use api\validators\PasswordRequiredValidator;
use common\helpers\Amqp;
use common\models\amqp\UsernameChanged;
use common\models\UsernameHistory;
use common\tasks\PullMojangUsername;
use common\validators\UsernameValidator;
use PhpAmqpLib\Message\AMQPMessage;
use Yii;
use yii\base\ErrorException;
class ChangeUsernameForm extends AccountActionForm {
@@ -42,7 +39,6 @@ class ChangeUsernameForm extends AccountActionForm {
$transaction = Yii::$app->db->beginTransaction();
$oldNickname = $account->username;
$account->username = $this->username;
if (!$account->save()) {
throw new ThisShouldNotHappenException('Cannot save account model with new username');
@@ -52,36 +48,14 @@ class ChangeUsernameForm extends AccountActionForm {
$usernamesHistory->account_id = $account->id;
$usernamesHistory->username = $account->username;
if (!$usernamesHistory->save()) {
throw new ErrorException('Cannot save username history record');
throw new ThisShouldNotHappenException('Cannot save username history record');
}
$this->createEventTask($account->id, $account->username, $oldNickname);
Yii::$app->queue->push(PullMojangUsername::createFromAccount($account));
$transaction->commit();
return true;
}
/**
* TODO: вынести это в отдельную сущность, т.к. эта команда используется внутри формы регистрации
*
* @param integer $accountId
* @param string $newNickname
* @param string $oldNickname
*
* @throws \PhpAmqpLib\Exception\AMQPExceptionInterface|\yii\base\Exception
*/
public function createEventTask($accountId, $newNickname, $oldNickname): void {
$model = new UsernameChanged();
$model->accountId = $accountId;
$model->oldUsername = $oldNickname;
$model->newUsername = $newNickname;
$message = Amqp::getInstance()->prepareMessage($model, [
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
]);
Amqp::sendToEventsExchange('accounts.username-changed', $message);
}
}

View File

@@ -1,13 +1,10 @@
<?php
namespace api\modules\accounts\models;
use api\exceptions\ThisShouldNotHappenException;
use api\modules\internal\helpers\Error as E;
use common\helpers\Amqp;
use common\models\Account;
use common\models\amqp\AccountPardoned;
use PhpAmqpLib\Message\AMQPMessage;
use Yii;
use yii\base\ErrorException;
class PardonAccountForm extends AccountActionForm {
@@ -33,25 +30,12 @@ class PardonAccountForm extends AccountActionForm {
$account = $this->getAccount();
$account->status = Account::STATUS_ACTIVE;
if (!$account->save()) {
throw new ErrorException('Cannot pardon account');
throw new ThisShouldNotHappenException('Cannot pardon account');
}
$this->createTask();
$transaction->commit();
return true;
}
public function createTask(): void {
$model = new AccountPardoned();
$model->accountId = $this->getAccount()->id;
$message = Amqp::getInstance()->prepareMessage($model, [
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
]);
Amqp::sendToEventsExchange('accounts.account-pardoned', $message);
}
}

View File

@@ -56,10 +56,9 @@ class RateLimiter extends \yii\filters\RateLimiter {
$ip = $request->getUserIP();
$key = $this->buildKey($ip);
$redis = $this->getRedis();
$countRequests = (int)$redis->incr($key);
$countRequests = (int)Yii::$app->redis->incr($key);
if ($countRequests === 1) {
$redis->executeCommand('EXPIRE', [$key, $this->limitTime]);
Yii::$app->redis->expire($key, $this->limitTime);
}
if ($countRequests > $this->limit) {
@@ -67,13 +66,6 @@ class RateLimiter extends \yii\filters\RateLimiter {
}
}
/**
* @return \common\components\Redis\Connection
*/
public function getRedis() {
return Yii::$app->redis;
}
/**
* @param Request $request
* @return OauthClient|null

View File

@@ -19,7 +19,7 @@ class SessionModel {
public static function find(string $username, string $serverId): ?self {
$key = static::buildKey($username, $serverId);
$result = Yii::$app->redis->executeCommand('GET', [$key]);
$result = Yii::$app->redis->get($key);
if (!$result) {
return null;
}
@@ -36,11 +36,11 @@ class SessionModel {
'serverId' => $this->serverId,
]);
return Yii::$app->redis->executeCommand('SETEX', [$key, self::KEY_TIME, $data]);
return Yii::$app->redis->setex($key, self::KEY_TIME, $data);
}
public function delete() {
return Yii::$app->redis->executeCommand('DEL', [static::buildKey($this->username, $this->serverId)]);
return Yii::$app->redis->del(static::buildKey($this->username, $this->serverId));
}
public function getAccount(): ?Account {