Тест на вакансию

Работа с Artemis в Docker-контейнере при помощи Perl

5 августа 2026 г.
132

Docker Compose файл

Создадим файл docker-compose.yml:
services:
  artemis:
    image: apache/activemq-artemis:latest
    container_name: artemis
    ports:
      - "61613:61613"   # STOMP порт
      - "61616:61616"   # Мультипротокольный порт (опционально)
      - "8161:8161"     # Веб-консоль
    environment:
      - ARTEMIS_USER=admin
      - ARTEMIS_PASSWORD=admin
    volumes:
      - artemis-data:/var/lib/artemis/data
      - artemis-logs:/var/lib/artemis/log
    restart: unless-stopped
volumes:
  artemis-data:
    driver: local
  artemis-logs:
    driver: local
Выполним команду в терминале из папки с docker-compose.yml. Флаг -d запускает контейнеры в фоновом режиме.
docker-compose up -d

Архитектура системы

ArtemisMQ Broker — это высокопроизводительный брокер сообщений на базе Apache ActiveMQ Artemis. В нашем случае используется официальный образ apache/activemq-artemis:latest, который предоставляет гибкую платформу для обмена сообщениями с поддержкой множества протоколов.

Сетевые порты

  • Порт 61613 (STOMP): используется приложениями для отправки и получения сообщений через протокол STOMP (Simple Text Oriented Messaging Protocol). Этот протокол особенно удобен для веб-приложений и клиентов на JavaScript.
  • Порт 61616 (мультипротокольный): основной порт для подключения по протоколам OpenWire, AMQP, MQTT и другим. Обеспечивает максимальную совместимость с различными клиентами.
  • Порт 8161 (веб-консоль): предназначен для веб-интерфейса управления Artemis, где вы можете администрировать очереди, темы и наблюдать за работой брокера.

Веб-консоль управления

Встроенный веб-интерфейс Artemis — это мощный инструмент для мониторинга и администрирования.

  • Адрес: http://localhost:8161
  • Логин/Пароль: admin/admin (заданы в переменных окружения)
  • Функциональность:
    • Просмотр всех очередей, тем и адресов.
    • Управление сообщениями: отправка, перемещение, удаление.
    • Мониторинг количества сообщений в очередях, скорости обработки и задержек.
    • Просмотр статистики по подключениям и сессиям.
    • Управление пользователями и правами доступа.

Хранение данных

Для обеспечения сохранности данных и журналов используются два постоянных тома (volumes):

  • artemis-data: хранит файлы данных брокера, включая все сохраняемые сообщения и метаданные очередей. Это гарантирует, что при перезапуске контейнера сообщения не будут потеряны.
  • artemis-logs: содержит журналы работы брокера, которые помогают в диагностике проблем и аудите операций.

Perl скрипты

Producer (отправитель для одного сообщения)

Создадим файл producer.pl. Этот скрипт на Perl демонстрирует пример производителя (producer) сообщений, который подключается к брокеру ArtemisMQ по протоколу STOMP и отправляет сообщение в очередь.
#!/usr/bin/perl

use strict;
use warnings;
use Net::STOMP::Client;

# --- Конфигурация подключения ---
my $STOMP_HOST = 'localhost';
my $STOMP_PORT = 61613;
my $STOMP_USER = 'admin';
my $STOMP_PASS = 'admin';
my $DESTINATION = '/queue/MyPerlQueue';  # Имя очереди

# --- Подключение к брокеру ---
my $stomp = Net::STOMP::Client->new(
    host => $STOMP_HOST,
    port => $STOMP_PORT,
);

# --- Аутентификация ---
$stomp->connect(
    login    => $STOMP_USER,
    passcode => $STOMP_PASS,
);

# --- Отправка сообщения ---
$stomp->send(
    destination => $DESTINATION,
    body        => 'Hello from Perl producer!',
    'destination-type' => 'ANYCAST',  # Для очередей
    'persistent'       => 'true',     # Постоянное сообщение
    'priority'         => '4',        # Приоритет от 0 до 9
    'expires'          => '0',        # Бесконечное время жизни
);

print "[OK] Message sent to queue '$DESTINATION'\n";

# --- Отключение ---
$stomp->disconnect();

exit 0;
Заголовки (headers):
  • destination-type: ANYCAST — указывает Artemis, что сообщение адресовано очереди (Anycast-семантика), а не теме (Multicast).
  • persistent: true — сообщение будет сохранено на диск для надежности.
  • priority: 4 — приоритет сообщения (от 0 — низший, до 9 — высший).
  • expires: 0 — сообщение не имеет срока годности (хранится бесконечно).

Consumer (получатель для одного сообщения)

Создадим файл consumer.pl. Этот скрипт на Perl демонстрирует пример потребителя (consumer) сообщений, который подключается к брокеру ArtemisMQ по протоколу STOMP, подписывается на очередь и получает из неё самое раннее (первое) сообщение с ручным подтверждением.
#!/usr/bin/perl

use strict;
use warnings;
use Net::STOMP::Client;

# --- Конфигурация подключения ---
my $STOMP_HOST = 'localhost';
my $STOMP_PORT = 61613;
my $STOMP_USER = 'admin';
my $STOMP_PASS = 'admin';
my $DESTINATION = '/queue/MyPerlQueue';

# --- Подключение к брокеру ---
my $stomp = Net::STOMP::Client->new(
    host => $STOMP_HOST,
    port => $STOMP_PORT,
);

# --- Аутентификация ---
$stomp->connect(
    login    => $STOMP_USER,
    passcode => $STOMP_PASS,
);

# --- Подписка на очередь ---
$stomp->subscribe(
    destination => $DESTINATION,
    'subscription-type' => 'ANYCAST',   # Для очередей
    'ack'               => 'client',    # Ручное подтверждение
);

print "[INFO] Waiting for messages from '$DESTINATION'...\n";

# --- Получение одного сообщения ---
my $frame = $stomp->receive_frame();

if ($frame && $frame->command eq 'MESSAGE') {
    my $body = $frame->body;
    print "[RECEIVED] $body\n";
   
    # Получаем ID для подтверждения
    my $ack_id = $frame->headers->{'ack'};
   
    if ($ack_id) {
        eval { $stomp->ack('id' => $ack_id) };
        if ($@) {
            print "[WARNING] ACK failed: $@\n";
        } else {
            print "[ACK] Message acknowledged (ID: $ack_id)\n";
        }
    }
} else {
    print "[ERROR] No message received\n";
}

# --- Отключение ---
$stomp->disconnect();

exit 0;

Заголовки (headers):
  • subscription-type: ANYCAST — указывает Artemis, что подписка осуществляется на очередь (Anycast-семантика), а не на тему.
  • ack: client — включает ручной режим подтверждения. Сообщение не будет удалено из очереди, пока клиент явно не отправит ACK.

Producer (отправитель для нескольких сообщений)

Создадим файл producer_loop.pl. Этот скрипт демонстрирует производителя, который отправляет 10 сообщений в топик (а не в очередь). Он использует семантику MULTICAST, чтобы каждое сообщение было доставлено всем активным подписчикам.

#!/usr/bin/perl

use strict;
use warnings;
use Net::STOMP::Client;
use Time::HiRes qw(sleep);  # Для паузы между сообщениями

# --- Конфигурация ---
my $STOMP_HOST = 'localhost';
my $STOMP_PORT = 61613;
my $STOMP_USER = 'admin';
my $STOMP_PASS = 'admin';
my $DESTINATION = '/topic/MyPerlTopic';
my $MESSAGES_COUNT = 10;  # Количество сообщений

# --- Подключение ---
my $stomp = Net::STOMP::Client->new(
    host => $STOMP_HOST,
    port => $STOMP_PORT,
);
$stomp->connect(
    login    => $STOMP_USER,
    passcode => $STOMP_PASS,
);

print "Sending $MESSAGES_COUNT messages to topic...\n";

# --- Цикл отправки ---
for my $i (1 .. $MESSAGES_COUNT) {
    my $body = "Message #$i sent to topic at " . localtime(time);
   
    $stomp->send(
        destination => $DESTINATION,
        body        => $body,
        'destination-type' => 'MULTICAST',    # Для топиков (все подписчики получают)
        'persistent'       => 'true',         # Сохранять сообщения на диск
        'priority'         => '4',            # Приоритет от 0 до 9
        'expires'          => '0',            # Бесконечное время жизни
        'auto-delete'      => 'false',        # Не удалять тему автоматически
        'JMSCorrelationID' => "msg-$i",       # Идентификатор для отслеживания
        'content-type'     => 'text/plain',   # Тип содержимого
    );
   
    print "  Sent: $body\n";
    sleep 0.5;  # Небольшая пауза между отправками
}

print "[OK] All messages sent to topic\n";

# --- Отключение ---
$stomp->disconnect();

exit 0;
Заголовки (headers) и их назначение:
  • destination-type: MULTICAST — ключевое отличие от очереди. Указывает Artemis, что сообщение адресовано топику (Multicast-семантика). Все подписчики этого топика получат копию сообщения.
  • persistent: true — сообщение будет сохранено на диск для надежности.
  • priority: 4 — приоритет сообщения (от 0 — низший, до 9 — высший).
  • expires: 0 — сообщение не имеет срока годности (хранится бесконечно).
  • auto-delete: false — предотвращает автоматическое удаление адреса топика, когда последний потребитель отписывается.
  • JMSCorrelationID: "msg-$i" — уникальный идентификатор для каждого сообщения, который можно использовать для отслеживания корреляции между запросами и ответами.
  • content-type: text/plain — явно указывает тип содержимого тела сообщения.

Consumer (получатель нескольких сообщений)

Создадим файл consumer_loop.pl. Этот скрипт демонстрирует потребителя, который подписывается на топик и бесконечно слушает входящие сообщения. В отличие от очереди, каждый активный потребитель топика получит свою копию каждого отправленного сообщения.

#!/usr/bin/perl

use strict;
use warnings;
use Net::STOMP::Client;

# --- Конфигурация подключения ---
my $STOMP_HOST = 'localhost';
my $STOMP_PORT = 61613;
my $STOMP_USER = 'admin';
my $STOMP_PASS = 'admin';
my $DESTINATION = '/topic/MyPerlTopic';

# --- Подключение к брокеру ---
my $stomp = Net::STOMP::Client->new(
    host => $STOMP_HOST,
    port => $STOMP_PORT,
);

# --- Аутентификация ---
$stomp->connect(
    login    => $STOMP_USER,
    passcode => $STOMP_PASS,
);

# --- Подписка на топик ---
$stomp->subscribe(
    destination => $DESTINATION,
    headers     => {
        'subscription-type' => 'MULTICAST',
        'ack'               => 'client',
    },
);

print "[INFO] Listening for messages from '$DESTINATION' (Press Ctrl+C to stop)...\n";

# --- Получение нескольких сообщений ---
while (1) {
    my $frame = $stomp->receive_frame();
   
    if ($frame && $frame->command eq 'MESSAGE') {
        my $body = $frame->body;
        my $message_id = $frame->headers->{'message-id'} || 'unknown';
       
        print "[RECEIVED] ID: $message_id, Body: $body\n";
       
        # Получаем ID для подтверждения
        my $ack_id = $frame->headers->{'ack'};
       
        if ($ack_id) {
            $stomp->ack(headers => { 'id' => $ack_id });
            print "[ACK] Message $message_id acknowledged\n";
        }
    }
}

# --- Отключение ---
$stomp->disconnect();
 

Заголовки (headers) и их назначение:
  • subscription-type: MULTICAST — указывает Artemis, что подписка осуществляется на топик (Multicast-семантика). Это гарантирует получение всех сообщений, отправленных в этот топик.
  • ack: client — включает ручной режим подтверждения. Сообщение не будет удалено из топика (для durable-подписчиков), пока клиент явно не отправит ACK. Это дает гарантию, что сообщение обработано успешно.

Работа с SSL

Для начала создадим сертификаты для подписчика и сервера (create_certs.bat):
@echo off
if not exist ".\certs" mkdir certs
cd certs

REM 1. Корневой CA
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 3650 -key ca.key -out ca.cer -subj "/C=RU/ST=Moscow/L=Moscow/O=MyOrg/CN=Artemis CA"

REM 2. Сертификат сервера Artemis
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/C=RU/ST=Moscow/L=Moscow/O=MyOrg/CN=localhost"
openssl x509 -req -days 3650 -in server.csr -CA ca.cer -CAkey ca.key -set_serial 01 -out server.cer

REM 3. Упаковываем ключ и сертификат сервера в PKCS12 KeyStore ===
openssl pkcs12 -export -in server.cer -inkey server.key -out server.p12 -name "artemis" -passout pass:changeit

REM 4. Упаковываем CA в PKCS12 TrustStore (если требуется проверка клиентов) ===
openssl pkcs12 -export -nokeys -in ca.cer -out truststore.p12 -name "caroot" -passout pass:changeit

REM 5. Сертификат клиента (для Perl)
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/C=RU/ST=Moscow/L=Moscow/O=MyOrg/CN=perl-client"
openssl x509 -req -days 3650 -in client.csr -CA ca.cer -CAkey ca.key -set_serial 02 -out client.cer

echo Certificates and KeyStores have been created!
pause

В параметрах -subj указан CN=localhost. Это критично, потому что Perl-скрипт будет подключаться к localhost. Если вы укажете другое имя, проверка сертификата на стороне клиента завершится ошибкой. Аналогичный под linux (create_certs.sh):
#!/bin/bash

# Создаем директорию для сертификатов
if [ ! -d "./certs" ]; then
    mkdir certs
fi
cd certs

# 1. Корневой CA
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 3650 -key ca.key -out ca.cer -subj "/C=RU/ST=Moscow/L=Moscow/O=MyOrg/CN=Artemis CA"

# 2. Сертификат сервера Artemis
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/C=RU/ST=Moscow/L=Moscow/O=MyOrg/CN=localhost"
openssl x509 -req -days 3650 -in server.csr -CA ca.cer -CAkey ca.key -set_serial 01 -out server.cer

# 3. Упаковываем ключ и сертификат сервера в PKCS12 KeyStore
openssl pkcs12 -export -in server.cer -inkey server.key -out server.p12 -name "artemis" -passout pass:changeit

# 4. Упаковываем CA в PKCS12 TrustStore (если требуется проверка клиентов)
openssl pkcs12 -export -nokeys -in ca.cer -out truststore.p12 -name "caroot" -passout pass:changeit

# 5. Сертификат клиента (для Perl)
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/C=RU/ST=Moscow/L=Moscow/O=MyOrg/CN=perl-client"
openssl x509 -req -days 3650 -in client.csr -CA ca.cer -CAkey ca.key -set_serial 02 -out client.cer

echo "Certificates and KeyStores have been created!"
 
Выполним один из файлов создания сертификатов. Изменим файл docker-compose.yml (первый вариант с копированием файлов в контейнер):
services:
  artemis:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: artemis
    ports:
      - "61613:61613"
      - "61614:61614"
      - "61616:61616"
      - "8161:8161"
    environment:
      - ARTEMIS_USER=admin
      - ARTEMIS_PASSWORD=admin
    volumes:
      - artemis-data:/var/lib/artemis/data
      - artemis-logs:/var/lib/artemis/log
    restart: unless-stopped
volumes:
  artemis-data:
    driver: local
  artemis-logs:
    driver: local
И сам Dockerfile:
FROM apache/activemq-artemis:latest

# Переключаемся на root для создания директорий
USER root

# Создаем папку для сертификатов
RUN mkdir -p /opt/artemis/etc/certs

# Копируем broker.xml
COPY broker.xml /var/lib/artemis-instance/etc-override/broker.xml

# Копируем сертификаты
COPY certs/ /opt/artemis/etc/certs/

# Меняем владельца (чтобы Artemis мог читать файлы)
RUN chown -R artemis:artemis /opt/artemis/etc/certs
RUN chmod 600 /opt/artemis/etc/certs/*.key
USER artemis

# Выводим содержимое для проверки (опционально)
RUN ls -la /opt/artemis/etc/certs/
Этот подход подходит для production-сред, где нужна самодостаточность образа. Для сборки образа и запуска:
docker-compose build --no-cache
docker-compose up -d 

Второй вариант с монтированием файлов (Dockerfile не нужен):
services:
  artemis:
    image: apache/activemq-artemis:latest
    container_name: artemis
    ports:
      - "61613:61613"
      - "61614:61614"
      - "61616:61616"
      - "8161:8161"
    environment:
      - ARTEMIS_USER=admin
      - ARTEMIS_PASSWORD=admin
    volumes:
      - artemis-data:/var/lib/artemis/data
      - artemis-logs:/var/lib/artemis/log
      - <ваш_путь_к_сертификатам>/certs:/opt/artemis/etc/certs:ro
      - <ваш_путь_к_broker.xml>/broker.xml:/var/lib/artemis-instance/etc-override/broker.xml:ro
    restart: unless-stopped
volumes:
  artemis-data:
    driver: local
  artemis-logs:
    driver: local
Этот подход подходит для разработки и сред, где сертификаты часто обновляются. Для запуска:
docker-compose up -d

broker.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd">
   <core xmlns="urn:activemq:core">
      <name>0.0.0.0</name>
      <persistence-enabled>true</persistence-enabled>
      <journal-type>ASYNCIO</journal-type>
      <paging-directory>./data/paging</paging-directory>
      <bindings-directory>./data/bindings</bindings-directory>
      <journal-directory>./data/journal</journal-directory>
      <large-messages-directory>./data/large-messages</large-messages-directory>
      <journal-datasync>true</journal-datasync>
      <journal-min-files>2</journal-min-files>
      <journal-pool-files>10</journal-pool-files>
      <journal-device-block-size>4096</journal-device-block-size>
      <journal-file-size>10M</journal-file-size>
      <journal-buffer-timeout>2000</journal-buffer-timeout>
      <disk-scan-period>5000</disk-scan-period>
      <max-disk-usage>90</max-disk-usage>
      <critical-analyzer>true</critical-analyzer>
      <critical-analyzer-timeout>120000</critical-analyzer-timeout>
      <critical-analyzer-check-period>60000</critical-analyzer-check-period>
      <critical-analyzer-policy>HALT</critical-analyzer-policy>
      <global-max-size>100MB</global-max-size>

      <acceptors>
         <!-- STOMP без SSL -->
         <acceptor name="stomp">tcp://0.0.0.0:61613?protocols=STOMP</acceptor>
         
         <!-- STOMP с SSL -->
         <acceptor name="stomp-ssl">tcp://0.0.0.0:61614?sslEnabled=true;keyStorePath=/opt/artemis/etc/certs/server.p12;keyStorePassword=changeit;keyStoreType=PKCS12;trustStorePath=/opt/artemis/etc/certs/truststore.p12;trustStorePassword=changeit;trustStoreType=PKCS12;enabledProtocols=TLSv1.2,TLSv1.3</acceptor>
         
         <!-- Мультипротокольный -->
         <acceptor name="all">tcp://0.0.0.0:61616?protocols=STOMP,AMQP,OPENWIRE</acceptor>
      </acceptors>

      <security-settings>
         <security-setting match="#">
            <permission type="createAddress" roles="admin,amq"/>
            <permission type="deleteAddress" roles="admin,amq"/>
            <permission type="createDurableQueue" roles="admin,amq"/>
            <permission type="deleteDurableQueue" roles="admin,amq"/>
            <permission type="createNonDurableQueue" roles="admin,amq"/>
            <permission type="deleteNonDurableQueue" roles="admin,amq"/>
            <permission type="send" roles="admin,amq"/>
            <permission type="consume" roles="admin,amq"/>
            <permission type="browse" roles="admin,amq"/>
            <permission type="manage" roles="admin,amq"/>
         </security-setting>
      </security-settings>

      <address-settings>
         <address-setting match="#">
            <dead-letter-address>DLQ</dead-letter-address>
            <expiry-address>ExpiryQueue</expiry-address>
            <redelivery-delay>0</redelivery-delay>
            <max-size-bytes>10485760</max-size-bytes>
            <message-counter-history-day-limit>10</message-counter-history-day-limit>
            <address-full-policy>BLOCK</address-full-policy>
            <auto-create-queues>true</auto-create-queues>
            <auto-create-addresses>true</auto-create-addresses>
            <auto-delete-queues>false</auto-delete-queues>
            <auto-delete-addresses>false</auto-delete-addresses>
         </address-setting>
      </address-settings>
   </core>
</configuration>
Краткое пояснение конфигурации broker.xml:
  • Acceptor stomp-ssl (порт 61614):
    • sslEnabled=true — включает защищённое SSL-соединение для STOMP-клиентов.
    • keyStorePath=/opt/artemis/etc/certs/server.p12 — путь к хранилищу серверного сертификата (PKCS12) для аутентификации перед клиентами.
    • trustStorePath=/opt/artemis/etc/certs/truststore.p12 — хранилище доверенных сертификатов для проверки клиентских сертификатов (взаимная аутентификация).
    • enabledProtocols=TLSv1.2,TLSv1.3 — разрешены только современные версии TLS для безопасности.
  • Acceptor stomp (порт 61613):
    • Незащищённое соединение без SSL — используется для отладки или локальных окружений без требований к шифрованию.
  • Acceptor all (порт 61616):
    • Поддерживает одновременно STOMP, AMQP и OpenWire — универсальный порт для разнородных клиентов.
  • Security-settings match="#":
    • Правила безопасности для всех адресов (# — wildcard).
    • Роли admin и amq имеют полный набор прав: создание/удаление очередей и адресов, отправка, потребление, просмотр и управление.
  • Address-settings match="#":
    • dead-letter-address: DLQ — недоставленные сообщения перенаправляются в очередь DLQ.
    • expiry-address: ExpiryQueue — просроченные сообщения попадают в очередь ExpiryQueue.
    • max-size-bytes: 10485760 (10 МБ) — максимальный размер адреса до применения политики BLOCK.
    • address-full-policy: BLOCK — при заполнении адреса входящие сообщения блокируются (не теряются).
    • auto-create-queues: true — очереди создаются автоматически при первом обращении (удобно для разработки).
    • auto-delete-queues: false — очереди не удаляются автоматически, даже после отписки всех потребителей (сохраняются до ручного удаления).

Producer (отправитель SSL)

Создадим файл producer_ssl.pl. Этот скрипт демонстрирует производителя, который отправляет одно JSON-сообщение в очередь (а не в топик) с использованием защищённого SSL-соединения. Он использует семантику ANYCAST для очередей, обеспечивая доставку сообщения в режиме "точка-точка" одному из активных потребителей. Скрипт настраивает взаимную аутентификацию через клиентские сертификаты и проверку подлинности сервера, гарантируя безопасную передачу данных. Сообщение содержит временную метку, идентификатор пользователя и текстовое поле, а также дополнительные STOMP-заголовки для управления приоритетом, персистентностью и типом контента. Процесс завершается корректным отключением от брокера после отправки.
 
#!/usr/bin/perl

use strict;
use warnings;
use Net::STOMP::Client;
use JSON;
use IO::Socket::SSL;

# --- Конфигурация ---
my $STOMP_HOST = 'localhost';
my $STOMP_PORT = 61614;
my $STOMP_USER = 'admin';
my $STOMP_PASS = 'admin';
my $DESTINATION = '/queue/MyQueueSSL';

# --- Пути к файлам сертификатов ---
my $CLIENT_CERT_FILE = '<ваш_путь_к_сертификатам>/certs/client.cer';
my $CLIENT_KEY_FILE = '<ваш_путь_к_сертификатам>/certs/client.key';
my $CA_CERT_FILE = '<ваш_путь_к_сертификатам>/certs/ca.cer';

# --- Подключение с SSL ---
my $stomp = Net::STOMP::Client->new(
    host => $STOMP_HOST,
    port => $STOMP_PORT,
    sockopts => {
        SSL_ca_file => $CA_CERT_FILE,
        SSL_cert_file => $CLIENT_CERT_FILE,
        SSL_key_file => $CLIENT_KEY_FILE,
        SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_PEER,
        SSL_version => 'TLSv1_2',
        SSL_verifycn_name => 'localhost',
    },
);

# --- Аутентификация ---
$stomp->connect(
    login    => $STOMP_USER,
    passcode => $STOMP_PASS,
);

# --- Отправка JSON-сообщения ---
my $data = {
    timestamp => time(),
    user      => 'perl-producer',
    message   => 'Hello Artemis with SSL!',
};
$stomp->send(
    destination => $DESTINATION,
    body        => encode_json($data),
    'destination-type' => 'ANYCAST',  # Для очередей
    'persistent'       => 'true',     # Постоянное сообщение
    'priority'         => '4',        # Приоритет от 0 до 9
    'expires'          => '0',        # Бесконечное время жизни
    'content-type'     => 'application/json',
);

print "JSON-message sent with SSL\n";

# --- Отключение ---
$stomp->disconnect();

exit 0;

Consumer (получатель нескольких сообщений SSL)

Создадим файл consumer_ssl.pl. Этот скрипт демонстрирует защищённого потребителя, который подписывается на очередь и бесконечно слушает входящие сообщения через SSL-соединение. В отличие от топика, очередь использует семантику ANYCAST, где каждое сообщение доставляется только одному из активных потребителей в режиме "точка-точка". Скрипт настраивает взаимную аутентификацию через сертификаты, использует ручное подтверждение (client-ack) для гарантированной доставки и автоматически декодирует JSON-сообщения, выводя их структуру через Data::Dumper. Потребитель работает в бесконечном цикле, обрабатывая входящие фреймы, подтверждая получение каждого сообщения и корректно обрабатывая возможные ошибки парсинга JSON.
 
#!/usr/bin/perl

use strict;
use warnings;
use Net::STOMP::Client;
use JSON;
use Data::Dumper;
use IO::Socket::SSL;

# --- Конфигурация подключения ---
my $STOMP_HOST = 'localhost';
my $STOMP_PORT = 61614;
my $STOMP_USER = 'admin';
my $STOMP_PASS = 'admin';
my $DESTINATION = '/queue/MyQueueSSL';

# --- Пути к сертификатам ---
my $CLIENT_CERT_FILE = '<ваш_путь_к_сертификатам>/certs/client.cer';
my $CLIENT_KEY_FILE = '<ваш_путь_к_сертификатам>/certs/client.key';
my $CA_CERT_FILE = '<ваш_путь_к_сертификатам>/certs/ca.cer';

# --- Подключение к брокеру с SSL ---
my $stomp = Net::STOMP::Client->new(
    host => $STOMP_HOST,
    port => $STOMP_PORT,
    sockopts => {
        SSL_ca_file => $CA_CERT_FILE,
        SSL_cert_file => $CLIENT_CERT_FILE,
        SSL_key_file => $CLIENT_KEY_FILE,
        SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_PEER,
        SSL_version => 'TLSv1_2',
        SSL_verifycn_name => 'localhost',
        # Для отладки:
        # SSL_debug => 1,
    },
);

# --- Аутентификация ---
$stomp->connect(
    login    => $STOMP_USER,
    passcode => $STOMP_PASS,
);

# --- Подписка ---
$stomp->subscribe(
    destination => $DESTINATION,
    'subscription-type' => 'ANYCAST',   # Для очередей
    'ack'               => 'client',    # Ручное подтверждение
);

print "[INFO] Listening for messages on SSL port $STOMP_PORT from '$DESTINATION' (Press Ctrl+C to stop)...\n";

# --- Получение нескольких сообщений ---
while (1) {
    my $frame = $stomp->receive_frame();
   
    if ($frame && $frame->command eq 'MESSAGE') {
        my $body = $frame->body;
        my $message_id = $frame->headers->{'message-id'} || 'unknown';
        my $content_type = $frame->headers->{'content-type'} || 'text/plain';
       
        if ($content_type eq 'application/json') {
            eval {
                my $data = decode_json($body);
                print "[JSON] ";
                print Dumper($data);
            };
            if ($@) {
                print "[ERROR] Invalid JSON: $@\n";
                print "[RAW] $body\n";
            }
        } else {
            print "[TEXT] $body\n";
        }
        # Получаем ID для подтверждения
        my $ack_id = $frame->headers->{'ack'};
       
        if ($ack_id) {
            eval { $stomp->ack('id' => $ack_id) };
            if ($@) {
                print "[WARNING] ACK failed: $@\n";
            } else {
                print "[ACK] Message acknowledged (ID: $ack_id)\n";
            }
        }
    }
}

# --- Отключение ---
$stomp->disconnect();

Итого

Мы рассмотрели три варианта docker-compose.yml для развертывания ArtemisMQ в зависимости от потребностей окружения:
  • Базовый — запуск официального образа с минимальной настройкой для быстрого старта.
  • SSL (сборка образа) — использование Dockerfile для копирования сертификатов и переопределённого broker.xml внутрь контейнера. Подходит для production-сред, где важна самодостаточность образа.
  • SSL (монтирование томов) — подключение сертификатов и конфигурации через volumes без пересборки образа. Удобно для разработки и частого обновления сертификатов.
Все варианты запускаются одной командой docker-compose up -d, не требуя сложной ручной установки брокера.

Использование портов (61613 для STOMP без шифрования, 61614 для защищённого STOMP через SSL, 61616 для мультипротокольных подключений и 8161 для веб-консоли) обеспечивает гибкость при разработке приложений с различными протоколами, безопасную передачу данных и удобный мониторинг через встроенный веб-интерфейс.

Примеры на Perl с использованием библиотеки Net::STOMP::Client демонстрируют базовые, но рабочие шаблоны для создания Producer (отправителя) и Consumer (получателя) как для очередей (Anycast), так и для топиков (Multicast), с поддержкой ручного подтверждения и SSL-шифрования для безопасного обмена сообщениями.
Поделиться: