Merge pull request 'zhimolostnova_anna_lab 4 complete' (#55) from zhimolostnova_anna_lab_4 into main
Reviewed-on: #55
This commit is contained in:
commit
f8ac151629
5
zhimolostnova_anna_lab_4/FirstTutorial/go.mod
Normal file
5
zhimolostnova_anna_lab_4/FirstTutorial/go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module FirstTutorial
|
||||
|
||||
go 1.23.2
|
||||
|
||||
require github.com/rabbitmq/amqp091-go v1.10.0
|
4
zhimolostnova_anna_lab_4/FirstTutorial/go.sum
Normal file
4
zhimolostnova_anna_lab_4/FirstTutorial/go.sum
Normal file
@ -0,0 +1,4 @@
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
65
zhimolostnova_anna_lab_4/FirstTutorial/receiver/receive.go
Normal file
65
zhimolostnova_anna_lab_4/FirstTutorial/receiver/receive.go
Normal file
@ -0,0 +1,65 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
|
||||
func failOnError(err error, msg string) {
|
||||
if err != nil {
|
||||
log.Panicf("%s: %s", msg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
|
||||
failOnError(err, "Failed to connect to RabbitMQ")
|
||||
defer func(conn *amqp.Connection) {
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(conn)
|
||||
|
||||
ch, err := conn.Channel()
|
||||
failOnError(err, "Failed to open a channel")
|
||||
defer func(ch *amqp.Channel) {
|
||||
err := ch.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(ch)
|
||||
|
||||
q, err := ch.QueueDeclare(
|
||||
"hello", // name
|
||||
false, // durable
|
||||
false, // delete when unused
|
||||
false, // exclusive
|
||||
false, // no-wait
|
||||
nil, // arguments
|
||||
)
|
||||
failOnError(err, "Failed to declare a queue")
|
||||
|
||||
msgs, err := ch.Consume(
|
||||
q.Name, // queue
|
||||
"", // consumer
|
||||
true, // auto-ack
|
||||
false, // exclusive
|
||||
false, // no-local
|
||||
false, // no-wait
|
||||
nil, // args
|
||||
)
|
||||
failOnError(err, "Failed to register a consumer")
|
||||
|
||||
var forever chan struct{}
|
||||
|
||||
go func() {
|
||||
for d := range msgs {
|
||||
log.Printf("Received a message: %s", d.Body)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
|
||||
<-forever
|
||||
}
|
63
zhimolostnova_anna_lab_4/FirstTutorial/sender/send.go
Normal file
63
zhimolostnova_anna_lab_4/FirstTutorial/sender/send.go
Normal file
@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
|
||||
func failOnError(err error, msg string) {
|
||||
if err != nil {
|
||||
log.Panicf("%s: %s", msg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
|
||||
failOnError(err, "Failed to connect to RabbitMQ")
|
||||
defer func(conn *amqp.Connection) {
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(conn)
|
||||
|
||||
ch, err := conn.Channel()
|
||||
failOnError(err, "Failed to open a channel")
|
||||
defer func(ch *amqp.Channel) {
|
||||
err := ch.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(ch)
|
||||
|
||||
q, err := ch.QueueDeclare(
|
||||
"hello", // name
|
||||
false, // durable
|
||||
false, // delete when unused
|
||||
false, // exclusive
|
||||
false, // no-wait
|
||||
nil, // arguments
|
||||
)
|
||||
failOnError(err, "Failed to declare a queue")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
for d := range 10 {
|
||||
body := "This message for consumer by number " + strconv.Itoa(d+1)
|
||||
err = ch.PublishWithContext(ctx,
|
||||
"", // exchange
|
||||
q.Name, // routing key
|
||||
false, // mandatory
|
||||
false, // immediate
|
||||
amqp.Publishing{
|
||||
ContentType: "text/plain",
|
||||
Body: []byte(body),
|
||||
})
|
||||
failOnError(err, "Failed to publish a message")
|
||||
log.Printf(" [x] Sent %s\n", body)
|
||||
}
|
||||
}
|
80
zhimolostnova_anna_lab_4/README.md
Normal file
80
zhimolostnova_anna_lab_4/README.md
Normal file
@ -0,0 +1,80 @@
|
||||
# Отчет по лабораторной работе №4
|
||||
|
||||
## Поставленные задачи
|
||||
|
||||
1. Установить брокер сообщений RabbitMQ.
|
||||
2. Пройти уроки 1, 2 и 3 из RabbitMQ Tutorials на любом языке программирования.
|
||||
3. Продемонстрировать работу брокера сообщений.
|
||||
|
||||
## Предметная область
|
||||
|
||||
Уведомление о штрафах за нарушение ПДД.
|
||||
|
||||
## Запуск работы
|
||||
|
||||
1. Убедиться, что установлены необходимые технологии:
|
||||
- **Docker**: Платформа для контейнеризации приложений.
|
||||
- **RabbitMQ**: Брокер сообщений.
|
||||
- **Docker Compose**: Инструмент для запуска многоконтейнерных приложений на основе `docker-compose.yaml`. Обычно поставляется вместе с Docker. Чтобы проверить, установлена ли утилита, нужно запустить команду:
|
||||
```bash
|
||||
docker-compose --version
|
||||
```
|
||||
|
||||
2. В директории, где находится файл `docker-compose.yaml`, выполнить следующую команду для запуска всех сервисов:
|
||||
```bash
|
||||
docker-compose up --build
|
||||
```
|
||||
Эта команда сначала выполнит сборку, а затем запустит контейнеры.
|
||||
|
||||
3. После успешного запуска можно перейти на RabbitMQ Management UI:
|
||||
- RabbitMQ Management UI: [http://localhost:15672/#/](http://localhost:15672/#/).
|
||||
|
||||
## Технологии
|
||||
|
||||
1. Golang: основной язык программирования.
|
||||
2. Docker & Docker Compose: для контейнеризации сервисов и удобного развертывания.
|
||||
3. RabbitMQ: брокер сообщений.
|
||||
|
||||
## Анализ полученных данных
|
||||
|
||||
![SlowQueueResult.png](ReportImages%2FSlowQueueResult.png)
|
||||
Для замедленной очереди можно сделать следующие выводы:
|
||||
- Все 366 сообщений доставлены потребителям, но они еще не подтверждены.
|
||||
Поскольку "Ready" равен нулю, это означает, что все сообщения сразу передаются потребителям,
|
||||
и ни одно сообщение не ожидает очереди.
|
||||
- Однако, поскольку все 366 сообщений находятся в состоянии "Unacked",
|
||||
это указывает на то, что потребитель не подтверждает обработку сообщений вовремя,
|
||||
что связано с медленной обработкой (как предполагает название очереди slow_consumer_queue).
|
||||
- Публикация сообщений идет с постоянной скоростью 1 сообщение в секунду,
|
||||
и с той же скоростью сообщения доставляются потребителю.
|
||||
- Однако скорость подтверждения (Consumer ack) составляет лишь 0.60 сообщений в секунду,
|
||||
что означает, что потребитель обрабатывает и подтверждает сообщения медленнее, чем они поступают.
|
||||
Это вызывает накопление неподтвержденных сообщений в очереди (Unacked), и со временем их число
|
||||
может продолжать расти, если скорость обработки не увеличится.
|
||||
|
||||
![FastQueueResult.png](ReportImages%2FFastQueueResult.png)
|
||||
Для быстрой очереди можно сделать следующие выводы:
|
||||
- Очередь пуста. Т.е. нет сообщений, которые ожидают обработки или находятся в процессе обработки.
|
||||
Это означает, что потребитель обрабатывает сообщения сразу после их получения, и сообщения не
|
||||
накапливаются в очереди.
|
||||
- Сообщения публикуются и доставляются потребителю со скоростью 1 сообщение в секунду, и
|
||||
потребитель подтверждает обработку каждого сообщения также со скоростью 1 сообщение в секунду.
|
||||
- Поскольку скорость подтверждения и доставки совпадают, сообщений не накапливается, и все
|
||||
обрабатывается своевременно.
|
||||
|
||||
## Ход работы
|
||||
|
||||
1. **Прохождение первого урока**: директория FirstTutorial
|
||||
![FirstTutorial.png](ReportImages%2FFirstTutorial.png)
|
||||
|
||||
2. **Прохождение второго урока**: директория SecondTutorial
|
||||
![SecondTutorial.png](ReportImages%2FSecondTutorial.png)
|
||||
|
||||
3. **Прохождение третьего урока и выполнение задания на ЛР №3**: директория ThirdTutorial
|
||||
![ThirdTutorial.png](ReportImages%2FThirdTutorial.png)
|
||||
|
||||
В коде присутствуют пояснительные комментарии.
|
||||
|
||||
## Демонстрационное видео
|
||||
|
||||
Видеозапись доступна по адресу: [https://vk.com/video193898050_456240872](https://vk.com/video193898050_456240872)
|
BIN
zhimolostnova_anna_lab_4/ReportImages/FastQueueResult.png
Normal file
BIN
zhimolostnova_anna_lab_4/ReportImages/FastQueueResult.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
zhimolostnova_anna_lab_4/ReportImages/FirstTutorial.png
Normal file
BIN
zhimolostnova_anna_lab_4/ReportImages/FirstTutorial.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 253 KiB |
BIN
zhimolostnova_anna_lab_4/ReportImages/SecondTutorial.png
Normal file
BIN
zhimolostnova_anna_lab_4/ReportImages/SecondTutorial.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 245 KiB |
BIN
zhimolostnova_anna_lab_4/ReportImages/SlowQueueResult.png
Normal file
BIN
zhimolostnova_anna_lab_4/ReportImages/SlowQueueResult.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
BIN
zhimolostnova_anna_lab_4/ReportImages/ThirdTutorial.png
Normal file
BIN
zhimolostnova_anna_lab_4/ReportImages/ThirdTutorial.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 223 KiB |
5
zhimolostnova_anna_lab_4/SecondTutorial/go.mod
Normal file
5
zhimolostnova_anna_lab_4/SecondTutorial/go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module SecondTutorial
|
||||
|
||||
go 1.23.2
|
||||
|
||||
require github.com/rabbitmq/amqp091-go v1.10.0
|
4
zhimolostnova_anna_lab_4/SecondTutorial/go.sum
Normal file
4
zhimolostnova_anna_lab_4/SecondTutorial/go.sum
Normal file
@ -0,0 +1,4 @@
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
74
zhimolostnova_anna_lab_4/SecondTutorial/tasks/new_task.go
Normal file
74
zhimolostnova_anna_lab_4/SecondTutorial/tasks/new_task.go
Normal file
@ -0,0 +1,74 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
|
||||
func failOnError(err error, msg string) {
|
||||
if err != nil {
|
||||
log.Panicf("%s: %s", msg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
|
||||
failOnError(err, "Failed to connect to RabbitMQ")
|
||||
defer func(conn *amqp.Connection) {
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(conn)
|
||||
|
||||
ch, err := conn.Channel()
|
||||
failOnError(err, "Failed to open a channel")
|
||||
defer func(ch *amqp.Channel) {
|
||||
err := ch.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(ch)
|
||||
|
||||
q, err := ch.QueueDeclare(
|
||||
"task", // name
|
||||
true, // durable
|
||||
false, // delete when unused
|
||||
false, // exclusive
|
||||
false, // no-wait
|
||||
nil, // arguments
|
||||
)
|
||||
failOnError(err, "Failed to declare a queue")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
body := bodyFrom(os.Args)
|
||||
err = ch.PublishWithContext(ctx,
|
||||
"", // exchange
|
||||
q.Name, // routing key
|
||||
false, // mandatory
|
||||
false,
|
||||
amqp.Publishing{
|
||||
DeliveryMode: amqp.Persistent,
|
||||
ContentType: "text/plain",
|
||||
Body: []byte(body),
|
||||
})
|
||||
failOnError(err, "Failed to publish a message")
|
||||
log.Printf(" [x] Sent %s", body)
|
||||
}
|
||||
|
||||
func bodyFrom(args []string) string {
|
||||
var s string
|
||||
if (len(args) < 2) || os.Args[1] == "" {
|
||||
s = "hello"
|
||||
} else {
|
||||
s = strings.Join(args[1:], " ")
|
||||
}
|
||||
return s
|
||||
}
|
82
zhimolostnova_anna_lab_4/SecondTutorial/workers/worker.go
Normal file
82
zhimolostnova_anna_lab_4/SecondTutorial/workers/worker.go
Normal file
@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
|
||||
func failOnError(err error, msg string) {
|
||||
if err != nil {
|
||||
log.Panicf("%s: %s", msg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
|
||||
failOnError(err, "Failed to connect to RabbitMQ")
|
||||
defer func(conn *amqp.Connection) {
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(conn)
|
||||
|
||||
ch, err := conn.Channel()
|
||||
failOnError(err, "Failed to open a channel")
|
||||
defer func(ch *amqp.Channel) {
|
||||
err := ch.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(ch)
|
||||
|
||||
q, err := ch.QueueDeclare(
|
||||
"task", // name
|
||||
true, // durable
|
||||
false, // delete when unused
|
||||
false, // exclusive
|
||||
false, // no-wait
|
||||
nil, // arguments
|
||||
)
|
||||
failOnError(err, "Failed to declare a queue")
|
||||
|
||||
err = ch.Qos(
|
||||
1, // prefetch count
|
||||
0, // prefetch size
|
||||
false, // global
|
||||
)
|
||||
failOnError(err, "Failed to set QoS")
|
||||
|
||||
msgs, err := ch.Consume(
|
||||
q.Name, // queue
|
||||
"", // consumer
|
||||
false, // auto-ack
|
||||
false, // exclusive
|
||||
false, // no-local
|
||||
false, // no-wait
|
||||
nil, // args
|
||||
)
|
||||
failOnError(err, "Failed to register a consumer")
|
||||
|
||||
var forever chan struct{}
|
||||
|
||||
go func() {
|
||||
for d := range msgs {
|
||||
log.Printf("Received a message: %s", d.Body)
|
||||
countChar := bytes.Count(d.Body, []byte(""))
|
||||
t := time.Duration(countChar)
|
||||
time.Sleep(t * time.Second)
|
||||
log.Printf("Done")
|
||||
err := d.Ack(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
|
||||
<-forever
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
# Пример для Consumer 1
|
||||
FROM golang:1.23
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем модули и загружаем зависимости
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Копируем исходный код
|
||||
COPY . .
|
||||
|
||||
# Сборка
|
||||
RUN go build -o firstConsumer .
|
||||
|
||||
# Запуск
|
||||
CMD ["./firstConsumer"]
|
@ -0,0 +1,92 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
|
||||
func failOnError(err error, msg string) {
|
||||
if err != nil {
|
||||
log.Panicf("%s: %s", msg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
conn, err := amqp.Dial("amqp://guest:guest@rabbitmq:5672/")
|
||||
failOnError(err, "Не удалось подключиться к RabbitMQ")
|
||||
defer func(conn *amqp.Connection) {
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(conn)
|
||||
|
||||
ch, err := conn.Channel()
|
||||
failOnError(err, "Не удалось открыть канал")
|
||||
defer func(ch *amqp.Channel) {
|
||||
err := ch.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(ch)
|
||||
|
||||
err = ch.ExchangeDeclare(
|
||||
"tax_notifications", // имя
|
||||
"fanout", // тип
|
||||
true, // durable
|
||||
false, // auto-deleted
|
||||
false, // internal
|
||||
false, // no-wait
|
||||
nil, // аргументы
|
||||
)
|
||||
failOnError(err, "Не удалось объявить exchange")
|
||||
|
||||
q, err := ch.QueueDeclare(
|
||||
"slow_consumer_queue", // имя очереди
|
||||
false, // durable
|
||||
false, // delete when unused
|
||||
false, // exclusive
|
||||
false, // no-wait
|
||||
nil, // аргументы
|
||||
)
|
||||
failOnError(err, "Не удалось объявить очередь")
|
||||
|
||||
err = ch.QueueBind(
|
||||
q.Name, // имя очереди
|
||||
"", // routing key
|
||||
"tax_notifications", // exchange
|
||||
false,
|
||||
nil,
|
||||
)
|
||||
failOnError(err, "Не удалось привязать очередь")
|
||||
|
||||
msgs, err := ch.Consume(
|
||||
q.Name, // очередь
|
||||
"", // firstConsumer
|
||||
false, // auto-ack
|
||||
false, // exclusive
|
||||
false, // no-local
|
||||
false, // no-wait
|
||||
nil, // аргументы
|
||||
)
|
||||
failOnError(err, "Не удалось зарегистрировать firstConsumer")
|
||||
|
||||
var forever chan struct{}
|
||||
|
||||
go func() {
|
||||
for d := range msgs {
|
||||
log.Printf(" [x] Получено: %s", d.Body)
|
||||
time.Sleep(2 * time.Second) // Задержка в 2 секунды
|
||||
log.Printf(" [x] Обработка завершена")
|
||||
err := d.Ack(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
log.Printf(" [*] Ожидание сообщений. Для выхода нажмите CTRL+C")
|
||||
<-forever
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
module firstConsumer
|
||||
|
||||
go 1.23.2
|
||||
|
||||
require github.com/rabbitmq/amqp091-go v1.10.0
|
@ -0,0 +1,4 @@
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
17
zhimolostnova_anna_lab_4/ThirdTutorial/publisher/Dockerfile
Normal file
17
zhimolostnova_anna_lab_4/ThirdTutorial/publisher/Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
# Пример для publisher
|
||||
FROM golang:1.23
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем модули и загружаем зависимости
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Копируем исходный код
|
||||
COPY . .
|
||||
|
||||
# Сборка
|
||||
RUN go build -o publisher .
|
||||
|
||||
# Запуск
|
||||
CMD ["./publisher"]
|
5
zhimolostnova_anna_lab_4/ThirdTutorial/publisher/go.mod
Normal file
5
zhimolostnova_anna_lab_4/ThirdTutorial/publisher/go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module publisher
|
||||
|
||||
go 1.23.2
|
||||
|
||||
require github.com/rabbitmq/amqp091-go v1.10.0
|
4
zhimolostnova_anna_lab_4/ThirdTutorial/publisher/go.sum
Normal file
4
zhimolostnova_anna_lab_4/ThirdTutorial/publisher/go.sum
Normal file
@ -0,0 +1,4 @@
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
|
||||
func failOnError(err error, msg string) {
|
||||
if err != nil {
|
||||
log.Panicf("%s: %s", msg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
conn, err := amqp.Dial("amqp://guest:guest@rabbitmq:5672/")
|
||||
failOnError(err, "Не удалось подключиться к RabbitMQ")
|
||||
defer func(conn *amqp.Connection) {
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(conn)
|
||||
|
||||
ch, err := conn.Channel()
|
||||
failOnError(err, "Не удалось открыть канал")
|
||||
defer func(ch *amqp.Channel) {
|
||||
err := ch.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(ch)
|
||||
|
||||
err = ch.ExchangeDeclare(
|
||||
"tax_notifications", // имя
|
||||
"fanout", // тип
|
||||
true, // durable
|
||||
false, // auto-deleted
|
||||
false, // internal
|
||||
false, // no-wait
|
||||
nil, // аргументы
|
||||
)
|
||||
failOnError(err, "Не удалось объявить exchange")
|
||||
|
||||
i := 1
|
||||
|
||||
for {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
body := "Уведомление о штрафе за нарушение ПДД №" + strconv.Itoa(i)
|
||||
err = ch.PublishWithContext(ctx,
|
||||
"tax_notifications", // exchange
|
||||
"", // routing key
|
||||
false, // mandatory
|
||||
false, // immediate
|
||||
amqp.Publishing{
|
||||
ContentType: "text/plain",
|
||||
Body: []byte(body),
|
||||
})
|
||||
failOnError(err, "Не удалось отправить сообщение")
|
||||
|
||||
log.Printf(" [x] Отправлено: %s", body)
|
||||
time.Sleep(1 * time.Second) // Генерация сообщений раз в секунду
|
||||
i++
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
# Пример для Consumer 1
|
||||
FROM golang:1.23
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем модули и загружаем зависимости
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Копируем исходный код
|
||||
COPY . .
|
||||
|
||||
# Сборка
|
||||
RUN go build -o secondConsumer .
|
||||
|
||||
# Запуск
|
||||
CMD ["./secondConsumer"]
|
@ -0,0 +1,5 @@
|
||||
module secondConsumer
|
||||
|
||||
go 1.23.2
|
||||
|
||||
require github.com/rabbitmq/amqp091-go v1.10.0
|
@ -0,0 +1,4 @@
|
||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
||||
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
@ -0,0 +1,90 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
amqp "github.com/rabbitmq/amqp091-go"
|
||||
)
|
||||
|
||||
func failError(err error, msg string) {
|
||||
if err != nil {
|
||||
log.Panicf("%s: %s", msg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
conn, err := amqp.Dial("amqp://guest:guest@rabbitmq:5672/")
|
||||
failError(err, "Не удалось подключиться к RabbitMQ")
|
||||
defer func(conn *amqp.Connection) {
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(conn)
|
||||
|
||||
ch, err := conn.Channel()
|
||||
failError(err, "Не удалось открыть канал")
|
||||
defer func(ch *amqp.Channel) {
|
||||
err := ch.Close()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
}(ch)
|
||||
|
||||
err = ch.ExchangeDeclare(
|
||||
"tax_notifications", // имя
|
||||
"fanout", // тип
|
||||
true, // durable
|
||||
false, // auto-deleted
|
||||
false, // internal
|
||||
false, // no-wait
|
||||
nil, // аргументы
|
||||
)
|
||||
failError(err, "Не удалось объявить exchange")
|
||||
|
||||
q, err := ch.QueueDeclare(
|
||||
"fast_consumer_queue", // имя очереди
|
||||
false, // durable
|
||||
false, // delete when unused
|
||||
false, // exclusive
|
||||
false, // no-wait
|
||||
nil, // аргументы
|
||||
)
|
||||
failError(err, "Не удалось объявить очередь")
|
||||
|
||||
err = ch.QueueBind(
|
||||
q.Name, // имя очереди
|
||||
"", // routing key
|
||||
"tax_notifications", // exchange
|
||||
false,
|
||||
nil,
|
||||
)
|
||||
failError(err, "Не удалось привязать очередь")
|
||||
|
||||
msgs, err := ch.Consume(
|
||||
q.Name, // очередь
|
||||
"", // firstConsumer
|
||||
false, // auto-ack
|
||||
false, // exclusive
|
||||
false, // no-local
|
||||
false, // no-wait
|
||||
nil, // аргументы
|
||||
)
|
||||
failError(err, "Не удалось зарегистрировать firstConsumer")
|
||||
|
||||
var forever chan struct{}
|
||||
|
||||
go func() {
|
||||
for d := range msgs {
|
||||
log.Printf(" [x] Получено: %s", d.Body)
|
||||
log.Printf(" [x] Обработка завершена")
|
||||
err := d.Ack(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
log.Printf(" [*] Ожидание сообщений. Для выхода нажмите CTRL+C")
|
||||
<-forever
|
||||
}
|
38
zhimolostnova_anna_lab_4/docker-compose.yml
Normal file
38
zhimolostnova_anna_lab_4/docker-compose.yml
Normal file
@ -0,0 +1,38 @@
|
||||
services:
|
||||
rabbitmq:
|
||||
image: rabbitmq:3-management
|
||||
ports:
|
||||
- "5672:5672"
|
||||
- "15672:15672"
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_USER: guest
|
||||
RABBITMQ_DEFAULT_PASS: guest
|
||||
healthcheck:
|
||||
test: ["CMD", "rabbitmqctl", "status"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
publisher:
|
||||
build: ThirdTutorial/publisher
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/
|
||||
|
||||
first_consumer:
|
||||
build: ThirdTutorial/firstConsumer
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/
|
||||
|
||||
second_consumer:
|
||||
build: ThirdTutorial/secondConsumer
|
||||
depends_on:
|
||||
rabbitmq:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/
|
Loading…
Reference in New Issue
Block a user