Jak na RabbitMQ v PHP?

PHP

RabbitMQ je skvělá věc. Objevil jsem ji poměrně nedávno (já vím ostuda 🤦‍♂️) – respektive jsem věděl o co přibližně jde, ale neměl jsem potřebu ji použít. Teď jsem ale programoval systém, který instaluje instance PHP aplikace na nové subdomény na vyžádání z jiného webu (serveru). Použít CRON zde nedává moc smysl, protože by běžel většinu času zbytečně a musel by se spouštět velmi často, aby spustil registraci co nejdříve to bude možné. Myslím, že RabbitMQ je i hezčí řešení. Krásně čeká, až se do fronty zařadí alespoň jeden požadavek a potom spustí požadovanou akci jednu po druhé (pokud se jich sejde víc).

Instalace na server

Instaloval jsem ji na VPS s Ubuntu 24.04 a Quick Start Script z RabbitMQ dokumentace fungoval výborně, až na 3 věci, které je potřeba si pohlídat

  1. Pozor na hostname – případně je potřeba
    nastavit v /etc/rabbitmq/rabbitmq-env.conf (ten defaultně neexistuje, takže vytvořit)
    a nastavit NODENAME=rabbit@localhost

  2. Nezapomenout povolit porty ve firewallu
    – ufw allow 15672- pokud chcete web management rozhraní přes příkaz
    rabbitmq-plugins enable rabbitmq_management
    – ufw allow 5672 – pokud chcete posílat do fronty z jiného serveru než na kterým je RabbitMQ nainstalovaný přes AMQP

  3. Vytvořit nového uživatele s pořádný heslem a smazat výchozího admin uživatele, který má jméno guest a heslo guest.
    
    
    rabbitmqctl add_user "jiny_admin" # přidá uživatele a vyžádá si zadání hesla
    rabbitmqctl set_user_tags jiny_admin administrator # nastaví uživateli "kategorii" administrator
    rabbitmqctl set_permissions -p / "jiny_admin" ".*" ".*" ".*" # nastaví uživateli práva
    rabbitmqctl list_users # oveřte si, že je opravdu vše nastavené
    rabbitmqctl delete_user 'guest' # smaže výhozího uživatele

PHP

Pro PHP má RabbitMQ composer balíček php-amqplib/php-amqplib, který se hodí jak pro vytváření položek do fronty, tak i na jejich vyřízení.

Nový požadavek do fronty

Toto jednoduše přidá do fronty se jménem „nazev_fronty“ nový požadavek v jehož obsahu je v ukázce e-mail.
U metody „queue_declare“ je potřeba si pohrát s parametry podle toho, co potřebujete. V ukázce je nastaveno: odolné vůči restaru serveru (to je jedno „true“).

<?php

require_once __DIR__ . '/vendor/autoload.php';

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

$connection = new AMQPStreamConnection('IP adresa serveru', 5672, 'jméno', 'heslo');
$channel = $connection->channel();

$channel->queue_declare('nazev_fronty', false, true, false, false);

$email = 'muj@email.cz';

$msg = new \PhpAmqpLib\Message\AMQPMessage($email);
$channel->basic_publish($msg, '', 'newapp');

$channel->close();
$connection->close();

Vyřízení požadavku

<?php

require_once __DIR__ . '/vendor/autoload.php';

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;


$connection = new AMQPStreamConnection('IP adresa serveru', 5673, 'jméno', 'heslo');
$channel = $connection->channel();

$channel->queue_declare('nazev_fronty', false, true, false, false);


$channel->basic_qos(null, 1, null); // Vezme 1 položku z fronty
$channel->basic_consume('nazev_fronty', '', false, false, false, false, function (AMQPMessage $message) {
    $email = $message->getBody();

    // zpracování - já zde třeba spouším shell skript pro instalaci PHP aplikace na novou subdoménu, instaluji SSL cesrtifikát a posílám info email, že vše dopadlo OK
 
    $message->getChannel()->basic_ack($message->getDeliveryTag()); // Označí se jako zpracované
});

// Díky této část se neukončí běh skriptu, ale vyčkává se na nový pořadavek ve frontě
while (count($channel->callbacks)) {
    $channel->wait();
}

$channel->close();
$this->connection->close();

PHP jako služba

Aby vyřizovací skript běžel vždy, nainstaluji si ho jako službu do serveru.

Vytvořím soubor /etc/systemd/system/nazev_sluzby.service, který definuje službu a jako příkaz spustí /bin/php /var/www/vyrizovaci_skript.php

[Unit]
Description=Název služby

[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/nazev_sluzby.pid
ExecStart=/bin/php /var/www/vyrizovaci_skript.php
KillMode=process

Restart=on-failure
RestartSec=42s

[Install]
WantedBy=default.target

Poté spustím službu a ověřím, že běží a je hotovo :)

systemctl enable nazev_sluzby
systemctl start nazev_sluzby
systemctl status nazev_sluzby

Znáte někoho, komu by článek mohl pomoct? Zasdílejte mu ho :)

Komentáře