\documentclass[a4paper, final]{article} %\usepackage{literat} % Нормальные шрифты \usepackage[14pt]{extsizes} % для того чтобы задать нестандартный 14-ый размер шрифта \usepackage{tabularx} \usepackage[T2A]{fontenc} \usepackage[utf8]{inputenc} \usepackage[russian]{babel} \usepackage{amsmath} \usepackage[left=25mm, top=20mm, right=20mm, bottom=20mm, footskip=10mm]{geometry} \usepackage{ragged2e} %для растягивания по ширине \usepackage{setspace} %для межстрочно го интервала \usepackage{moreverb} %для работы с листингами \usepackage{indentfirst} % для абзацного отступа \usepackage{moreverb} %для печати в листинге исходного кода программ \usepackage{pdfpages} %для вставки других pdf файлов \usepackage{tikz} \usepackage{graphicx} \usepackage{afterpage} \usepackage{longtable} \usepackage{float} \usepackage{tabularx} % \usepackage[paper=A4,DIV=12]{typearea} \usepackage{pdflscape} % \usepackage{lscape} \usepackage{array} \usepackage{multirow} \renewcommand\verbatimtabsize{4\relax} \renewcommand\listingoffset{0.2em} %отступ от номеров строк в листинге \renewcommand{\arraystretch}{1.4} % изменяю высоту строки в таблице \usepackage[font=small, singlelinecheck=false, justification=centering, format=plain, labelsep=period]{caption} %для настройки заголовка таблицы \usepackage{listings} %листинги \usepackage{xcolor} % цвета \usepackage{hyperref}% для гиперссылок \usepackage{enumitem} %для перечислений \newcommand{\specialcell}[2][l]{\begin{tabular}[#1]{@{}l@{}}#2\end{tabular}} \setlist[enumerate,itemize]{leftmargin=1.2cm} %отступ в перечислениях \hypersetup{colorlinks, allcolors=[RGB]{010 090 200}} %красивые гиперссылки (не красные) % подгружаемые языки — подробнее в документации listings (это всё для листингов) \lstloadlanguages{ SQL} % включаем кириллицу и добавляем кое−какие опции \lstset{tabsize=2, breaklines, basicstyle=\footnotesize, columns=fullflexible, flexiblecolumns, numbers=left, numberstyle={\footnotesize}, keywordstyle=\color{blue}, inputencoding=cp1251, extendedchars=true } \lstdefinelanguage{MyC}{ language=SQL, % ndkeywordstyle=\color{darkgray}\bfseries, % identifierstyle=\color{black}, % morecomment=[n]{/**}{*/}, % commentstyle=\color{blue}\ttfamily, % stringstyle=\color{red}\ttfamily, % morestring=[b]", % showstringspaces=false, % morecomment=[l][\color{gray}]{//}, keepspaces=true, escapechar=\%, texcl=true } \textheight=24cm % высота текста \textwidth=16cm % ширина текста \oddsidemargin=0pt % отступ от левого края \topmargin=-1.5cm % отступ от верхнего края \parindent=24pt % абзацный отступ \parskip=5pt % интервал между абзацами \tolerance=2000 % терпимость к "жидким" строкам \flushbottom % выравнивание высоты страниц % Настройка листингов \lstset{ language=C++, extendedchars=\true, inputencoding=utf8, keepspaces=true, % captionpos=b, % подписи листингов снизу } \begin{document} % начало документа % НАЧАЛО ТИТУЛЬНОГО ЛИСТА \begin{center} \hfill \break \hfill \break \normalsize{МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ\\ федеральное государственное автономное образовательное учреждение высшего образования «Санкт-Петербургский политехнический университет Петра Великого»\\[10pt]} \normalsize{Институт компьютерных наук и кибербезопасности}\\[10pt] \normalsize{Высшая школа технологий искусственного интеллекта}\\[10pt] \normalsize{Направление: 02.03.01 <<Математика и компьютерные науки>>}\\ \hfill \break \hfill \break \hfill \break \hfill \break \large{Лабораторная работа №3}\\ \large{<<Межпроцессорное взаимодействие с помощью RabbitMQ>>}\\ \large{по дисциплине <<Сети ЭВМ и телекоммуникации компьютерных сетей>>}\\ \hfill \break % \hfill \break \hfill \break \end{center} \small{ \begin{tabular}{lrrl} \!\!\!Студент, & \hspace{2cm} & & \\ \!\!\!группы 5130201/20102 & \hspace{2cm} & \underline{\hspace{3cm}} &Тищенко А. А. \\\\ \!\!\!Преподаватель & \hspace{2cm} & \underline{\hspace{3cm}} & Мулюха В. А. \\\\ &&\hspace{4cm} \end{tabular} \begin{flushright} <<\underline{\hspace{1cm}}>>\underline{\hspace{2.5cm}} 2025г. \end{flushright} } \hfill \break % \hfill \break \begin{center} \small{Санкт-Петербург, 2025} \end{center} \thispagestyle{empty} % выключаем отображение номера для этой страницы % КОНЕЦ ТИТУЛЬНОГО ЛИСТА % \newpage % \tableofcontents \newpage \section{Постановка задачи} Используя docker создать контейнеры, необходимые для реализации следующего функционала с использованием RabbitMQ, а также показать, как именно осуществляется передача в этих условиях: \textit{Вариант 10}. Организовать отправку сообщений от двух отправителей одному получателю, получатель читает сообщения значительно реже их отправки и происходит переполнение очереди. Задавать раличные стратегии переполнения очереди, показать в чем разница. \newpage \section{Ход работы} \subsection{Код программы получателя} Класс `Recv` из пакета `dev.tishenko.consumer` реализует потребителя сообщений из очереди RabbitMQ с именем `hello`. Он подключается к брокеру `rabbitmq`, создаёт очередь с ограниченной длиной (`x-max-length`) и стратегией переполнения (`x-overflow`), параметры которых настраиваются через переменные окружения. Консьюмер асинхронно принимает сообщения и после каждой обработки (с задержкой, заданной через `CONSUMER\_DELAY\_MS`) подтверждает получение сообщения. Благодаря настройке `basicQos(1)` сообщения обрабатываются по одному, что обеспечивает контроль нагрузки и предотвращает переполнение очереди необработанными сообщениями. \begin{lstlisting}[label={lst:}] package dev.tishenko.consumer; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.DeliverCallback; import java.util.HashMap; import java.util.Map; public class Recv { private final static String QUEUE_NAME = "hello"; public static void main(String[] argv) throws Exception { String delayEnv = System.getenv("CONSUMER_DELAY_MS"); int delay = delayEnv != null ? Integer.parseInt(delayEnv) : 5000; String maxLengthEnv = System.getenv("QUEUE_MAX_LENGTH"); int maxLength = maxLengthEnv != null ? Integer.parseInt(maxLengthEnv) : 5; String overflowStrategy = System.getenv("QUEUE_OVERFLOW"); if (overflowStrategy == null) { overflowStrategy = "drop-head"; // или "reject-publish" } ConnectionFactory factory = new ConnectionFactory(); factory.setHost("rabbitmq"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); Map args = new HashMap<>(); args.put("x-max-length", maxLength); args.put("x-overflow", overflowStrategy); channel.queueDeclare(QUEUE_NAME, false, false, false, args); channel.basicQos(1); System.out.println(" [*] Waiting for messages..."); DeliverCallback deliverCallback = (consumerTag, delivery) -> { String message = new String(delivery.getBody(), "UTF-8"); System.out.println(" [x] Received '" + message + "'"); try { Thread.sleep(delay); } catch (InterruptedException e) { e.printStackTrace(); } channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); }; channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> { }); } } \end{lstlisting} \subsection{Код программы отправителя} Класс `Send` представляет собой простого отправителя сообщений для RabbitMQ. Он подключается к брокеру (по адресу контейнера `rabbitmq`), использует очередь с именем `hello` (предполагая, что она уже существует), и в бесконечном цикле отправляет в неё сообщения с уникальным номером, указывая имя продюсера, полученное из переменной окружения `PRODUCER\_NAME`. Задержка между отправками настраивается через переменную `PRODUCER\_DELAY\_MS`. После каждой отправки ожидается подтверждение от брокера, иначе выводится предупреждение. \begin{lstlisting}[label={lst:}] package dev.tishenko.producer; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.Connection; import com.rabbitmq.client.Channel; public class Send { private static final String QUEUE_NAME = "hello"; public static void main(String[] args) throws Exception { String name = System.getenv("PRODUCER_NAME"); name = name != null ? name : ""; String delayEnv = System.getenv("PRODUCER_DELAY_MS"); int delay = delayEnv != null ? Integer.parseInt(delayEnv) : 1000; ConnectionFactory factory = new ConnectionFactory(); factory.setHost("rabbitmq"); try (Connection connection = factory.newConnection(); Channel channel = connection.createChannel()) { channel.queueDeclarePassive(QUEUE_NAME); channel.confirmSelect(); int count = 0; while (true) { String message = "Message from " + name + " #" + count++; channel.basicPublish("", QUEUE_NAME, null, message.getBytes()); if (!channel.waitForConfirms()) { System.out.println(" [!] Message was NOT confirmed!"); } else { System.out.println(" [x] Sent '" + message + "'"); } Thread.sleep(delay); } } } } \end{lstlisting} \subsection{Dockerfile для отправителя и получателя} Для программы отправителя и получателя сообщений были созданы два идентичных Dockerfile. Программы написаны на Java и собирались с помощью Gradle, поэтому используется базовый образ \texttt{gradle:8.13-jdk21}. \begin{lstlisting}[label={lst:}] FROM gradle:8.13-jdk21 WORKDIR /app COPY . . RUN gradle build --no-daemon CMD ["gradle", "run", "--no-daemon"] \end{lstlisting} \subsection{Настройка Docker Compose} Файл \texttt{docker-compose.yml} нужен, чтобы управлять сразу несколькими контейнерами. Файл, представленный ниже, состоит из следующих секций: \begin{itemize} \item \textbf{rabbitmq} -- контейнер с RabbitMQ. Используется образ \texttt{rabbitmq:4.0-management}, который включает веб-интерфейс для мониторинга. Порты 5672 и 15672 проброшены наружу для AMQP-протокола и веб-интерфейса соответственно. \item \textbf{producer1} -- первый продюсер, отправляющий сообщения в RabbitMQ. Сборка производится из локальной директории \texttt{./producer}. В переменных окружения задаётся имя продюсера (\texttt{PRODUCER\_NAME}) и задержка между отправкой сообщений в миллисекундах (\texttt{PRODUCER\_DELAY\_MS}). \item \textbf{producer2} -- второй продюсер, аналогичный первому. \item \textbf{consumer} -- получатель сообщений из очереди RabbitMQ. Сборка происходит из директории \texttt{./consumer}. Консьюмер обрабатывает сообщения с заданной задержкой (\texttt{CONSUMER\_DELAY\_MS}). Дополнительно задаются параметры очереди: максимальная длина (\texttt{QUEUE\_MAX\_LENGTH}) и поведение при переполнении (\texttt{QUEUE\_OVERFLOW}). \item \textbf{networks} -- определение виртуальной сети \texttt{rabbitnet}, через которую все сервисы обмениваются данными. Используется стандартный сетевой драйвер \texttt{bridge}. \end{itemize} \begin{lstlisting}[label={lst:}] services: rabbitmq: image: rabbitmq:4.0-management hostname: rabbitmq container_name: rabbitmq ports: - "5672:5672" - "15672:15672" networks: - rabbitnet producer1: container_name: producer1 build: context: ./producer depends_on: - rabbitmq - consumer environment: - PRODUCER_NAME=first - PRODUCER_DELAY_MS=1000 networks: - rabbitnet producer2: container_name: producer2 build: context: ./producer depends_on: - rabbitmq - consumer environment: - PRODUCER_NAME=second - PRODUCER_DELAY_MS=1000 networks: - rabbitnet consumer: container_name: consumer build: context: ./consumer depends_on: - rabbitmq environment: - CONSUMER_DELAY_MS=1000 - QUEUE_MAX_LENGTH=10 - QUEUE_OVERFLOW=reject-publish # drop-head, reject-publish networks: - rabbitnet networks: rabbitnet: driver: bridge \end{lstlisting} \section{Результат работы} Ниже представлены логи для 10 секунд работы контейнеров, в режиме \texttt{QUEUE\_OVERFLOW=drop-head}. Начиная с 25 строки видно, что получатель начинает получать сообщения только от первого отправителя, потому что сообщения второго просто удаляются. \begin{lstlisting}[label={lst:}] producer1 | [x] Sent 'Message from first #0' producer1 | [x] Sent 'Message from first #1' producer2 | [x] Sent 'Message from second #2' consumer | [x] Received 'Message from second #1' producer1 | [x] Sent 'Message from first #2' producer2 | [x] Sent 'Message from second #3' consumer | [x] Received 'Message from first #1' producer1 | [x] Sent 'Message from first #3' producer2 | [x] Sent 'Message from second #4' consumer | [x] Received 'Message from second #2' producer1 | [x] Sent 'Message from first #4' producer2 | [x] Sent 'Message from second #5' consumer | [x] Received 'Message from first #2' producer1 | [x] Sent 'Message from first #5' producer2 | [x] Sent 'Message from second #6' consumer | [x] Received 'Message from second #3' producer1 | [x] Sent 'Message from first #6' producer2 | [x] Sent 'Message from second #7' consumer | [x] Received 'Message from first #3' producer1 | [x] Sent 'Message from first #7' producer2 | [x] Sent 'Message from second #8' consumer | [x] Received 'Message from second #4' producer1 | [x] Sent 'Message from first #8' producer2 | [x] Sent 'Message from second #9' consumer | [x] Received 'Message from first #4' producer1 | [x] Sent 'Message from first #9' producer2 | [x] Sent 'Message from second #10' consumer | [x] Received 'Message from first #5' producer1 | [x] Sent 'Message from first #10' producer2 | [x] Sent 'Message from second #11' consumer | [x] Received 'Message from first #6' producer1 | [x] Sent 'Message from first #11' producer2 | [x] Sent 'Message from second #12' consumer | [x] Received 'Message from first #7' producer1 | [x] Sent 'Message from first #12' producer2 | [x] Sent 'Message from second #13' consumer | [x] Received 'Message from first #8' producer1 | [x] Sent 'Message from first #13' producer2 | [x] Sent 'Message from second #14' consumer | [x] Received 'Message from first #9' producer1 | [x] Sent 'Message from first #14' producer2 | [x] Sent 'Message from second #15' consumer | [x] Received 'Message from first #10' \end{lstlisting} Ниже представлены логи для 10 секунд работы контейнеров, в режиме \texttt{QUEUE\_OVERFLOW=reject-publish}. Начиная с 27 строки, второй отправитель выдаёт сообщение \texttt{Message was NOT confirmed!}, это означает, что он получает отказ от RabbitMQ и его сообщение не добавляется в очередь. \begin{lstlisting}[label={lst:}] producer1 | [x] Sent 'Message from first #0' producer1 | [x] Sent 'Message from first #1' consumer | [x] Received 'Message from second #1' producer2 | [x] Sent 'Message from second #2' producer1 | [x] Sent 'Message from first #2' consumer | [x] Received 'Message from first #1' producer2 | [x] Sent 'Message from second #3' producer1 | [x] Sent 'Message from first #3' consumer | [x] Received 'Message from second #2' producer2 | [x] Sent 'Message from second #4' producer1 | [x] Sent 'Message from first #4' consumer | [x] Received 'Message from first #2' producer2 | [x] Sent 'Message from second #5' producer1 | [x] Sent 'Message from first #5' consumer | [x] Received 'Message from second #3' producer2 | [x] Sent 'Message from second #6' producer1 | [x] Sent 'Message from first #6' consumer | [x] Received 'Message from first #3' producer2 | [x] Sent 'Message from second #7' producer1 | [x] Sent 'Message from first #7' producer2 | [x] Sent 'Message from second #8' producer1 | [x] Sent 'Message from first #8' consumer | [x] Received 'Message from second #4' producer2 | [x] Sent 'Message from second #9' producer1 | [x] Sent 'Message from first #9' consumer | [x] Received 'Message from first #4' producer2 | [!] Message was NOT confirmed! producer1 | [x] Sent 'Message from first #10' consumer | [x] Received 'Message from second #5' producer2 | [!] Message was NOT confirmed! producer1 | [x] Sent 'Message from first #11' consumer | [x] Received 'Message from first #5' producer2 | [!] Message was NOT confirmed! producer1 | [x] Sent 'Message from first #12' consumer | [x] Received 'Message from second #6' producer2 | [!] Message was NOT confirmed! producer1 | [x] Sent 'Message from first #13' consumer | [x] Received 'Message from first #6' producer1 | [x] Sent 'Message from first #14' consumer | [x] Received 'Message from second #7' producer2 | [!] Message was NOT confirmed! producer1 | [x] Sent 'Message from first #15' consumer | [x] Received 'Message from first #7' producer2 | [!] Message was NOT confirmed! producer1 | [x] Sent 'Message from first #16' consumer | [x] Received 'Message from second #8' producer2 | [!] Message was NOT confirmed! producer1 | [x] Sent 'Message from first #17' consumer | [x] Received 'Message from first #8' producer2 | [!] Message was NOT confirmed! consumer | [x] Received 'Message from second #9' producer2 | [!] Message was NOT confirmed! producer1 | [x] Sent 'Message from first #18' consumer | [x] Received 'Message from first #9' producer2 | [!] Message was NOT confirmed! producer1 | [x] Sent 'Message from first #19' consumer | [x] Received 'Message from first #10' \end{lstlisting} \end{document}