Блог Мазепина Василия

Пишу о том, что кажется интересным

Beanstalkd, давай дружить?

2015-11-08 22:00 | Комментарии

Доброго времени суток, коллеги и гости.

Как-то по работе мне засветила перспектива внедрить в один symfony-based проект, в котором активно используется symfony-console, диспетчер очередей. Необходимо это было для оптимизации выполняемых команд. Причем, к сожалению, этим диспетчером стал не горячо любимый заяц, а Beanstalkd. Сейчас времени под доработку не выделено, однако мне всё же захотелось потрогать эту штуку, попробовать понять принцип работы, захотелось установить и заюзать его в тестовом режиме. И мне это удалось, о чем я и расскажу. Теперь обо всем по порядку:

1. Установка

У меня в качестве рабочей машины стоит ubuntu. Согласно официальной документации, установка сервера производится очень легко:
sudo apt-get install beanstalkd

Но давайте не забывать о…

На рабочей машине, для тестирования новой приблуды не хотелось бы засорять свой рабочий ноутбук, поэтому я воспользуюсь своим же советом и создам lxc-контейнер, в котором и будет всё это окружение работать:
sudo lxc-create -n beanstalkd -t ubuntu
Далее войду в созданный контейнер, и создам там директорию под проект, например вот так:
/home/ubuntu/VIRTUAL_SERVER/test
Эта директория будет использоваться как проектная.
После этого установим beanstalkd уже знакомой нам командой:
sudo apt-get install beanstalkd
После этих манипуляций мы можем запустить диспетчер очередей:
beanstalkd -l 127.0.0.1 -p 11301 &
Обратите внимание, здесь указываются адрес и порт, если нужны не те, что по умолчанию
Общий список опций запуска можно вызвать так:
beanstalkd --help
Не слишком замудрено, это радует.

Ах да, совсем забыли…

Устанавливаем необходимый нам php5
sudo apt-get install php5
Теперь кажется готово, установка завершена. Осталось настроить маппинг директорий, чтобы править код на своем рабочем компьютере, а не внутри контейнера. Об этом я рассказывал ранее.

2. Понеслась…

Теперь, собственно говоря, перейдем в проект, и установим php-клиент для beanstalkd:
composer require pda/pheanstalk
Так как мне будет нужно работать с symfony-console, установлю и его:
composer require symfony/console
После этого мне необходимо настроить структуру проекта, чтобы приблизить тестовые испытания к тому, что ждет меня при выполнении боевой задачи. В общем, всё не так сложно: Кратко о структуре:

app/console.php - Symfony Console,

app/console.php
1
2
3
4
5
6
7
8
9
10
<?php

require_once __DIR__ . '/../bootstrap.php';
require_once __DIR__ . '/../Command/StartPheanstalkCommand.php';

use Symfony\Component\Console\Application;

$console = new Application();
$console->add(new StartPheanstalkCommand()); // Добавляем необходимую нам команду
$console->run();

Command/StartPheanstalkCommand.php - Команда чтения, которая реализует к примеру чтение очереди, название которой будет вводиться при запуске, c ней и будем работать:

Command/StartPheanstalkCommand.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use Pheanstalk\Pheanstalk;

class StartPheanstalkCommand extends \Symfony\Component\Console\Command\Command
{
    protected function configure()
    {
        $this
            ->setName('pheanstalk:start')
            ->setDescription('Start pheanstalk dispatcher')
            ->addArgument(
                'queue',
                InputArgument::REQUIRED,
                'What queue?'
            );
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $queue = $input->getArgument('queue');
        $pheanstalk = new Pheanstalk('127.0.0.1',11301); // Подключаюсь к диспетчеру очереди
        echo 'Читаю очередь...' . PHP_EOL;
        while($pheanstalk->getConnection()->isServiceListening()) { // Пока соединение с диспетчером активно
            $job = $pheanstalk
                ->watch($queue) // Подключаюсь к очереди "Банк"
                ->reserve(); // Отмечаю команду как находящуюся в обработке(чтобы другой слушатель ее не взял)
            echo $job->getData(); // Выведу содержимое команды
            $pheanstalk->delete($job); // Удаляю команду из очереди
        }
    }
}

bootstrap.php - Подключает необходимые зависимости.

bootstrap.php
1
2
<?php
require_once 'vendor/autoload.php';

upload.php - Этот файл мы будем запускать, чтобы класть что-либо в очередь

upload.php
1
2
3
4
5
6
<?php
require_once 'bootstrap.php';

use Pheanstalk\Pheanstalk;
$pheanstalk = new Pheanstalk('127.0.0.1',11301); // Подключаюсь к диспетчеру
$pheanstalk->useTube('bank')->put(date('Y-m-d H:i:s'). ' - Бабушка хочет пенсию' . PHP_EOL); // Отправляю бабушку в очередь

3. Поехали!!!

Итого у нас получается такой расклад:
Мы запускаем команду(создаем одного читателя очереди “Банк”):
В банк приходит бабушка:
php -f upload.php
В процессе видим: Поменяем содержимое upload.php:

upload.php
1
2
3
4
5
6
<?php
require_once 'bootstrap.php';

use Pheanstalk\Pheanstalk;
$pheanstalk = new Pheanstalk('127.0.0.1',11301); // Подключаюсь к диспетчеру
$pheanstalk->useTube('bank')->put(date('Y-m-d H:i:s'). ' - Дедушка хочет пенсию' . PHP_EOL); // Отправляю дедушку в очередь

Теперь в банк приходит дедушка:
php -f upload.php
Видим: Как нам стало ясно, теперь в любой точке проекта мы можем отправлять команды в очередь, и они будут обработаны, надо будет лишь создать слушателя этой очереди.

4. Что в итоге?

В итоге мы получили следуюшие результаты:

1. Установлен диспетчер очереди.
2. Установлена PHP-библиотека для этого зверя.
3. Эта библиотека прекрасно вписывается в “Symfony Console” компонент.
4. Мы наглядно увидели как работает это всё совместно.
5. И всё это сделано без вреда для рабочего компьютера с помощью LXC - контейнера!
6. Бабушка с дедушкой получат наконец долгожданную пенсию.

Нам никто не может запретить создавать несколько параллельных читателей для одной очереди, нам никто не мешает создавать несколько очередей.
И это прекрасно!!
Всем спасибо за то что дочитали до конца, жду отзывы и комментарии!

P.S.

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

Комментарии