From 6345c30e064fbe55742b3383b1ccd4ecbab2c6fb Mon Sep 17 00:00:00 2001 From: Arity-T Date: Wed, 16 Apr 2025 11:21:11 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D1=82=D1=87=D1=91=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lab3/report/.gitignore | 5 + lab3/report/report.tex | 464 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 469 insertions(+) create mode 100644 lab3/report/.gitignore create mode 100644 lab3/report/report.tex diff --git a/lab3/report/.gitignore b/lab3/report/.gitignore new file mode 100644 index 0000000..38a3719 --- /dev/null +++ b/lab3/report/.gitignore @@ -0,0 +1,5 @@ +**/* +!.gitignore +!report.tex +!img +!img/** \ No newline at end of file diff --git a/lab3/report/report.tex b/lab3/report/report.tex new file mode 100644 index 0000000..f75ec56 --- /dev/null +++ b/lab3/report/report.tex @@ -0,0 +1,464 @@ +\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} \ No newline at end of file