Files
nets/lab3/report/report.tex
2025-04-16 11:21:11 +03:00

464 lines
22 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\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<String, Object> 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}