Персональна освітня сорінка
by Pavlo Shcherbukha
Якось спробував Object Storage SeaweedFS для побудови SPARK + Delta Lake (правда поки не вдало, бо там проблема з адаптацією Python + Java Spark). Але ідея мені сподобался. Тепер я зацікавився архітектурними шаблонами: Redis, Rabbit MQ, Node.js/Flask web service чи backend worker з використаням локального Object Storage SeaweedFS.
SeaweedFS — це інструмент для локального та розподіленого S3-сховища (плюс у нього є власний Filer із POSIX-інтерфейсом). Його головна фішка — швидкий старт (один бінарник на Go) та сумісність з S3 API, що робить його ідеальним моком (тест замінником) для хмарних AWS S3 у локальному середовищі або k3s/Docker-пісочницях і транзакційність для файлів.
Для того, щоб зрозуміти, чому я звернувся до цієї теми, потрібно сказати, що мені не одноразово приходилося писати обробники для пакетного файлового обміну. В таких випадках є класичною проблема, коли одна програма пише файл, а друга його починає читати.Останній раз надійна обробка файлів у системах пакетного обміну файлами у мене получалася десь в кооперації з Sybase Power Builder та Novel Netware на початку 2000-них. Там була якась реальна файлова транзакційність і доступ до файлів з блокуванням. А от як пішли мережі Microsoft і всякі гібриди: давайте віртуалку linux поверх VmWare та розшаримо там диск… Воно все стало таким не надійним….
У Novell NetWare файлова система (NWFS) і блокування на рівні записів/файлів (File Locking / Record Locking) були реалізовані на рівні ядра мережевої ОС. Коли програма на PowerBuilder чатала файл чи писала, було присутє ексклюзивне блокування і ніхто у всій мережі не міг туди залізти до завершення транзакції - тобто до завершення роботи з файлом.
А потім прийшов епохальний «прогрес»: шари віртуалізації, мережеві диски (NFS, SMB/CIFS), які намагаються емулювати локальні файлові системи поверх нестабільних мереж, і різноманітні гібридні схеми. У результаті ми отримали класичні болі:
Мережеві таймаути та розриви зв’язку, через які файли застрягають на половині запису.
Race conditions, коли два воркери одночасно починають читати або писати один і той самий незавершений файл.
Злітання (пропадання) прав доступу (POSIX vs Windows ACLs) після кожного оновлення Docker-контейнера чи монтажу тому.
Використання S3-сумісного об’єктного сховища (як-от SeaweedFS) у парі з чергами повідомлень і кешем — це, по суті, сучасний реінкарнований патерн транзакційного зберігання для розподілених систем.
Він повертає ті самі гарантії надійності, але адаптовані під сучасні хмарні та контейнеризовані реалії:
Атомарність замість блокувань: Замість того, щоб блокувати файл на диску (що вбиває масштабування у Kubernetes), ми робимо HTTP PUT-запит в S3. Поки файл не завантажився повністю, його не існує для інших. Коли завантажився — він з’являється атомарно, з унікальним хешем (ETag).
Деталізація управління через шину: Redis тримає актуальний статус (state machine), RabbitMQ/MQ гарантує доставку задачі (at-least-once delivery), а SeaweedFS тримає важкий файл (важкий бінарник).
Stateless-архітектура: Будь-який воркер може впасти під час обробки — і нічого не станеться. Файл в S3 нікуди не зникне, задача в черзі повернеться (nack/requeue) і її підхопить інший воркер. Жодних пошкоджених файлів на спільних дисках.
Це не стільки стаття «про SeaweedFS», скільки спроба описати практичний шаблон інтеграції з системами, які диктують файловий інтерфейс:
не чіпати вендорську вертушку, а винести передавання файлів у контрольований шлюз;
не змушувати IBM ACE залежати від спільного диска та його монтувань.
Ідея статті — не в тому, що S3 магічно забезпечує транзакційність, а в тому, що об’єктне сховище може стати спільною точкою передачі, а файлова інфраструктура — замінитися простішим контрактом: завантажити об’єкт, передати його ідентифікатор, обробити, і далі видалити або зберегти.
Як на мене , то формулювання цього всього у вигляді чітких архітектурних шаблонів для корпоративних систем пакетного обміну — надзвичайно цінна річ, особливо коли доводиться пояснювати молодшим розробникам, чому не треба пхати файли в NFS-шари та писати кастомні сокети.
Далі я спробував розібрати різні сценарії, які мені зустрічалися. Можливо, я десь перебільшив - тут все залежить від конкретної задачі.
Часто вендори видають своїм клієнтам “програмі вертушки” (це якась закрита Java-програма або Legacy-бінарник ще з нульових, написана під Windows Server або забутий Linux) зазвичай ставиться за принципом “поставте на віртуалку, хай туди пише, а далі робіть що хочете”. Це класичний анекдот із життя інтеграцій з вендорами (і особливо з різноманітними процесингами, банківськими клірингами та банкоматними мережами, платіжними боксами … ). І за звичай, обслуговувати таку прогаму дають наймолодшому інфраструктурнику. Керівники ІТ не розумію всієї глибини катастрофи отаких вертушок.
І тут починається традиційний біль:
Диск переповнюється за вихідні, бо ніхто не налаштував ротацію логів чи старих файлів транзакцій (бо “недодевопс” забув про logrotate або cron-скрипт).
Беккапи не працюють, бо файли постійно відкриті на запис.
Моніторингу немає, поки банк не подзвонить і не скаже, що за вечір не прийшов жоден звіт про транзакції (або статус банкоматів “завис”).
Спробую описати вирішення проблеми за допомогою SeaweedFS через архітектурний шаблон «S3-Sidecar / Push Gateway для Legacy-вертушок»
Ми можемо за допомогою банальнгого curl копіювпти чи переносити файли на SeaweedFS і це буде надійніше, простіше і зрозуміліше ніж складні bash скрипти. Та і взагалі, перенести цю задачу на локальний (або віддалений) S3-ендпоїнт (SeaweedFS) за допомогою банального curl — це геніальне у своїй простоті й надійності рішення. Воно повністю нівелює людський фактор.
Як це працює на практиці:
Ізоляція віртуалки: Вертушка продовжує писати свої файли у локальну папку на віртуалці (як і вимагає вендор, щоб не зламати її внутрішню логіку).
Примітивний sidecar-скрипт (або гола задача в cron / Task Scheduler): Замість складних bash-скриптів із перевіркою зайнятості дисків, SSH/rsync на чужі сервери чи налаштування NFS, пишеться елементарний однорядковий виклик curl (або невеличкий пайтон-скрипт):
# Приклад відправки файлу транзакцій однією командою одразу після закриття сесії
curl -X PUT -T "/var/processor/out/transactions_2026-11-09.dat" \
"http://seaweed-s3.internal:8333/atm-processing-bucket/incoming/transactions_2026-11-09.dat"
Чому це кардинально змінює надійність:
Нуль залежностей від кваліфікації адміна: Написати або скопіювати один curl з правильним S3-шлюзом здатний навіть джуніор. Йому не треба пояснювати тонкощі налаштування прав доступу, прав на файлові системи чи розгортання складних клієнтів.
Імунітет до падінь мережі: Якщо зв’язок між віртуалкою та сховищем пропав, файл просто залишається лежати в локальній папці вертушки (вона не падає від помилки запису), а при наступному запуску скрипта curl робить чергову спробу відправки.
Єдина точка збору даних: Незалежно від того, скільки у вас таких віртуалок з різними вендорами й вертушками лежать у різних сегментах мережі, всі вони просто штовхають свої файли по єдиному стандартизованому HTTP/S3 протоколу в єдине сховище (SeaweedFS), звідки їх уже забирає централізований ETL-пайплайн чи корпоративна шина.
Це ідеальний приклад того, як сучасні легкі хмарні патерни (S3 endpoint) елегантно вирішують хронічні болі класичного корпоративного IT-зоопарку.
Ну, тут правда, знайдуться розумники, що на тій віртуалці піднімуть ftp чи sftp і скажуть забирайте через нього. Любителі піднімати vsftpd чи OpenSSH sftp-server на віртуалці вендора зазвичай щиро вірять, що «це ж стандартний протокол». Але на практиці вони отримують цілий букет класичних архітектурних граблів:
Коли зовнішня система або внутрішня вертушка заливає файл через FTP/SFTP, він спочатку з’являється на диску в процесі передачі (або під якимось тимчасовим ім’ям на кшталт .inprog_filename). Якщо ваш збирач (ETL або воркер) почне читати цей файл за розкладом до того, як передача завершилася — ви отримуєте биті дані. Щоб цього уникнути, вендори змушені налаштовувати трюки з перейменуванням (rename після завершення), але «недодевопси» про це часто забувають або налаштовують криво.
Якщо файл великий (наприклад, багатогодинний потік транзакцій чи знімок стану банкоматів), а воркер намагається забрати його паралельно з тим, як FTP-сервер його дописує, починаються стандартні системні блокування файлів на рівні ОС (file locking). Процеси починають чекати один одного, зависати або падати з таймаутами.
Якщо мережевий обрив стався посередині FTP-сесії, на диску віртуалки залишаються напівзавантажені гігабайтні шматки. Вони висітимуть там роками, поки хтось вручну їх не почистить (бо автоочищення ніхто не написав).
Заведення окремих системних користувачів, налаштування chroot для ізоляції каталогів, ротація SSH-ключів або підтримка старих алгоритмів шифрування (бо у вендора стара бібліотека) — усе це витрати часу, які не додають жодної бізнес-цінності.
Чому перехід на S3-шлюз (SeaweedFS) виграє у цьому порівнянні:
Атомарність з коробки: Команда PUT через HTTP/S3 в SeaweedFS гарантує, що файл з’явиться у сховищі тільки в цілісному вигляді. Споживач ніколи не прочитає «обрізаний» файл.
Відсутність блокувань читачів: Десять воркерів можуть одночасно читати один і той самий об’єкт з S3, і вони ніколи не заблокують процес запису, який робить інша система.
Простота моніторингу та стабільність: Замість складних сесій SFTP, які обриваються через таймаути фаєрволів, ви маєте простий і надійний протокол поверх HTTP з можливістю чітких ретраїв.
Як на мене, то формування такого підходу в чіткі архітектурні шаблони — це чудовий спосіб раз і назавжди позбавити команду головного болю з інтеграцією Legacy-систем.
Уявіть картину: раз на 15 хвилин кілька сотень (або тисяч) банкоматів по всій країні одночасно кидають у спільну папку (або штовхають через кривий FTP) свої state-файли на кшталт atm_status_100500_20261109_2100.dat.
Що робить стандартна «недодевопс»-архітектура на віртуалці:
Пише в той самий файл (або створює тисячі дрібних файлів), забиваючи диск.
Якийсь локальний скрипт намагається це згребти, але файл у цей момент заблокований процесом запису.
У результаті маємо гонки (race conditions), биті звіти, пропущені інкасації та моніторинг, який бреше операторам, що «все ок», хоча в касетах уже вітер гуляє.
Замість того, щоб змушувати вендорський софт боротися з файловими блокуваннями на диску, цю задачу ідеально закриває S3-сумісне сховище (SeaweedFS) у парі з легкою асинхронною обробкою:
Налаштовуємо скрипт-транспорт так, щоб він формував назву файлу з унікальним штампом часу та суфіксом (наприклад, atms/100500/2026-11-09T21:00:00Z.json). Жодного перезапису одного й того самого файлу! Кожне опитування банкомату — це новий незмінний (immutable) об’єкт у сховищі.
Банкомат (або його шлюз) може заливати свій статус хоч щомиті. Об’єкти лягають у SeaweedFS паралельно, бо в об’єктних сховищах немає поняття «перезапис рядка в середині файлу» — є лише створення чи заміна ключа.
RabbitMQ повідомляє воркерам (Node.js / Python Flask), що з’явився новий пакет статусів за такий-то період. Воркер забирає файл з S3, робить швидкий INSERT у часову базу даних (TimescaleDB) чи відправляє алерти, а оригінальний JSON залишається в сховищі як історичний аудиторський слід.
Це повністю позбавляє від головного болю з ротацією старих статусів, проблемою забитих дисків та блокувань файлів на віртуалках.
Північноамериканське «пхати великі бінарники в MQ» — це класичний архітектурний біль enterprise-систем. Усі брокери повідомлень (і IBM MQ, і RabbitMQ, і Kafka) оптимізовані під швидку доставку легких подій та метаданих, а не під зберігання файлів. Коли через MQ починають ганяти гігабайти, брокер швидко забиває пам’ять, падає лог транзакцій, а решта черг стають «у пробку».
Тобто класична проблема для всіх черг, не зважаючи на вендора: В чергу пишемо тільки метадані (URL великого бінарного об’єкту), а сам об’єкт зберігається на S3-сумісному сховищі SeaweedFS.
Складнощі адміністрування: Потреба примапити NFS/SMB-диск до контейнера або віртуалки з ACE, налаштувати права (mqbrkrs або відповідний UID), слідкувати за ротацією та монтуванням у Kubernetes/Docker.
Блокування та конкурентність: Якщо кілька інстансів (реплік) IBM ACE читають одну пачку файлів з файлової системи, починаються гонки за файлами (file locking), дублювання обробки або помилки доступу.
Масштабування: Горизонтально масштабувати воркери ACE стає складно, бо кожен інстанс має бачити спільну файлову систему з однаковими правами.
Замість того, щоб змушувати IBM ACE «тягнути» файл по складних CIFS/NFS-шляхах з локальних дисків, потоковий потік перетворюється на чисту ізольовану шину:
Зовнішнє завантаження/вивантаження: Клієнт чи попередній сервіс завантажує файл напряму в SeaweedFS по S3 API (отримуючи швидкий локальний S3-ендпоїнт).
Легке повідомлення в IBM MQ: В IBM MQ лежить компактне повідомлення (JSON/XML) з посиланням на бакет та ключ об’єкта (bucket, objectKey).
JavaCompute або ESQL скрипт в ACE: Усередині потоку ACE (наприклад, через JavaCompute node) потік за кілька мілісекунд забирає файл за HTTP/S3-запитом, обробляє його (трансформує в БД, валідує чи штовхає далі) і видаляє або переносить у сховищі.
Жодних блокувань і проблем із правами на диски: Усі контейнери ACE стають stateless — вони нічого не пишуть на локальний диск, а працюють виключно з пам’яттю та мережевими викликами.
Це робить архітектуру надзвичайно стійкою до пікових навантажень (коли летить багато великих файлів одразу).
Горизонтальне масштабування (Cluster Hell): Якщо ви запускаєте кілька екземплярів Node-RED для балансування навантаження чи високої доступності, локальна папка перестає бути спільною. Один інстанс записав файл, а інший контейнер його «не бачить», бо у них ізольовані файлові системи (якщо не прикручений складний NFS/PVC).
Конфлікти запису та читання: Нода watch або file in може спрацювати до того, як великий файл повністю допишеться на диск, що призведе до битих JSON-об’єктів чи неповних бінарників.
Переповнення диску: У разі збою воркера сміття залишається на диску вічно, якщо не писати складні скрипти очищення.
У Node-RED є чудова екосистема нод для роботи з S3 (наприклад, пакети на кшталт node-red-contrib-s3 або банальний http request / стандартні axios/fetch усередині ноди function).
Коли ви використовуєте SeaweedFS як локальний S3-ендпоїнт для Node-RED:
Stateless-воркери: Ваші флоу Node-RED стають повністю незалежними. Будь-який контейнер Node-RED може прийняти payload, кинути його в SeaweedFS і передати далі легкий json (ключ об’єкта) через MQTT, RabbitMQ або навіть внутрішній link / subflow.
Атомарність: У S3 об’єкт з’являється лише тоді, коли він повністю завантажений. Жодних проблем із «напівзаписаними» файлами, які читає воркер.
Однакова логіка для локальної розробки та прод-середовища: Коли ви пишете код на своєму ноутбуці, SeaweedFS крутиться в сусідньому контейнері на http://localhost:8333. Коли флоу їде на корпоративний сервер (де є справжній AWS S3 чи MinIO), ви не змінюєте жодного рядка коду чи ноди — міняється лише endpoint у конфігурації.
Це, мабуть, класика жанру, яка зазвичай закінчується написанням власного протоколу поверх TCP, де починаються проблеми з фрагментацією пакетів, обривами зв’язку, відсутністю ретраїв та падіннями пам’яті при переповненні буферів.
Перехід на S3-ендпоїнт (як-от SeaweedFS) вирішує ці проблеми на рівні архітектурних гарантій:
У класичній файловій системі або при передачі через сирий сокет файл дописується шматками. Якщо воркер читає його паралельно (або процес обірвався посередині), ви отримуєте битий JSON або зіпсований бінарник. У протоколі S3 операція PUT є атомарною: об’єкт з’являється у сховищі тільки тоді, коли він повністю завантажений і пройшов перевірку контрольної суми (MD5 / ETag). До цього моменту його для споживачів просто не існує. Жодних блокувань і гонок (race conditions).
Control Plane (Redis + RabbitMQ): Передає лише легкі метадані та інструкції керування (що зробити, де взяти, який статус). Це швидко, надійно і не навантажує мережу.
Data Plane (SeaweedFS S3): Приймає важкий контент натомість через стандартний, оптимізований HTTP/S3-протокол, який має вбудовані таймаути, коннект-пули, кешування та можливість докачки (range requests).
В результаті код і флоу стають чистими, передбачуваними і повністю втрачають потребу в будь-яких кастомних мережевих костилях.
Проблема: Передавати важкі корисні навантаження (великі JSON-документи, бінарні файли, PDF, скомпільовані звіти, дампи) через RabbitMQ — антипатерн. Це забиває брокер, роздуває пам’ять і гальмує споживачів. Як вирішити через SeaweedFS:
Producer (Node.js/Flask): Коли надходить великий об’єкт (або задача з важким контекстом), сервіс завантажує балк в SeaweedFS по S3 API (або через SeaweedFS Filer HTTP API).
Message (RabbitMQ): У чергу летить лише легке повідомлення-посилання:
{
"task_id": "uuid-1234",
"storage_bucket": "temp-processing",
"storage_key": "incoming/payload_uuid-1234.json",
"checksum": "sha256:..."
}
Проблема: Звертатися до S3/SeaweedFS за списком об’єктів або перевіряти їх наявність під час високого навантаження — зайві мережеві затримки. Як вирішити через SeaweedFS + Redis:
HSET doc:uuid-1234 status "processing" s3_path "seaweed://bucket/path/file.bin" updated_at "1762725600"
Оскільки SeaweedFS підтримує стандартний AWS S3 API (Endpoint http://localhost:8333), його неймовірно зручно використовувати в автоматизованих інтеграційних тестах (Jest для Node.js або Pytest для Flask).
Пatерн підняття в Docker Compose: У docker-compose.test.yml піднімаємо мінімальний інстанс SeaweedFS (master + volume) разом з RabbitMQ та Redis.
Ініціалізація бакетів при старті тестів: Перед запуском тестів скрипт ініціалізації створює тестові бакети в SeaweedFS.
Перевага перед Moto/MinIO: SeaweedFS значно швидший на старті та менш «вибагливий» до ресурсів у порівнянні з важкими аналогами, при цьому надійно імітує реальну поведінку S3.
SeaweedFS має унікальну особливість — Filer, який окрім S3 дозволяє монтувати сховище через WebDAV, gRPC або навіть через FUSE (локальна папка).
Для Flask/Node.js воркерів: Якщо ваш воркер генерує звіти (наприклад, PDF або великі XLS/CSV експорти), ви можете налаштувати SeaweedFS Filer так, щоб він був доступний як звичайна локальна директорія через мережу або Docker-волюм (/mnt/seaweedfs/exports/…).
Воркер просто пише файл на диск (fs.writeFileSync або стандартний Python open()), а SeaweedFS сам прозоро розбиває його на блоки і розкидує по сховищу. Жодних складних S3-клієнтів усередині бізнес-логіки не потрібно.
Ось готовий стек для локальної розробки та інтеграційного тестування. Він включає повний docker-compose.yml, де підняті SeaweedFS (Master + Volume + S3 API), Redis, RabbitMQ (разом із системою керування), а також приклад Node.js сервісу, який демонструє патерн S3-Offloading (коли важкий payload лежить у SeaweedFS, а в RabbitMQ лежить лише посилання).
version: '3.8'
services:
# 1. Redis для стейтів, кешування та статусів задач
redis:
image: redis:7-alpine
container_name: local_redis
ports:
- "6379:6379"
restart: unless-stopped
# 2. RabbitMQ для черги повідомлень
rabbitmq:
image: rabbitmq:3.12-management-alpine
container_name: local_rabbitmq
ports:
- "5672:5672" # AMQP порт
- "15672:15672" # Management UI (http://localhost:15672, guest/guest)
restart: unless-stopped
# 3. SeaweedFS S3-сумісне сховище (Master + Volume)
seaweedfs:
image: chrislusf/seaweedfs:latest
container_name: local_seaweedfs
command: "server -dir=/data -s3 -s3.port=8333"
ports:
- "9333:9333" # Master port
- "8080:8080" # Filer port (опціонально)
- "8333:8333" # S3 API port
volumes:
- seaweed_data:/data
restart: unless-stopped
volumes:
seaweed_data:
Цей приклад реалізує базовий сервіс на Express.js та amqplib із використанням офіційного AWS SDK (@aws-sdk/client-s3), налаштованого на локальний ендпоїнт SeaweedFS.
Структура пректу
app/
├── package.json
└── index.js
package.json
{
"name": "seaweed-rabbitmq-redis-demo",
"version": "1.0.0",
"type": "module",
"dependencies": {
"@aws-sdk/client-s3": "^3.500.0",
"amqplib": "^10.3.0",
"express": "^4.19.2",
"redis": "^4.6.13",
"uuid": "^9.0.1"
}
}
index.js - програмний код
import express from 'express';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import amqp from 'amqplib';
import { createClient } from 'redis';
import { v4 as uuidv4 } from 'uuid';
const app = express();
app.use(express.json());
// --- 1. ПІДКЛЮЧЕННЯ ДО СЕРВІСІВ ---
// Конфігурація SeaweedFS S3 API (Endpoint: http://localhost:8333)
const s3 = new S3Client({
region: 'us-east-1',
endpoint: 'http://localhost:8333',
forcePathStyle: true, // Обов'язково для SeaweedFS/MinIO
credentials: {
accessKeyId: 'any_key',
secretAccessKey: 'any_secret',
},
});
const BUCKET_NAME = 'app-payloads';
// Redis клієнт
const redisClient = createClient({ url: 'redis://localhost:6379' });
await redisClient.connect();
// RabbitMQ підключення та ініціалізація черги
let rabbitChannel, rabbitConnection;
async function initRabbitMQ() {
rabbitConnection = await amqp.connect('amqp://localhost:5672');
rabbitChannel = await rabbitConnection.createChannel();
await rabbitChannel.assertQueue('task_queue', { durable: true });
console.log(' Connected to RabbitMQ & Queue asserted');
}
await initRabbitMQ();
// --- 2. ENDPOINT: СТВОРЕННЯ ЗАДАЧІ (Producer) ---
app.post('/api/tasks', async (req, res) => {
try {
const taskId = uuidv4();
const largePayload = {
taskId,
createdAt: new Date().toISOString(),
data: req.body.data || "Heavy payload data simulation...",
};
const s3Key = `tasks/${taskId}/payload.json`;
// Крок А: Зберігаємо важкий об'єкт у SeaweedFS S3
await s3.send(new PutObjectCommand({
Bucket: BUCKET_NAME,
Key: s3Key,
Body: JSON.stringify(largePayload),
ContentType: 'application/json',
}));
// Крок Б: Записуємо початковий стан у Redis
await redisClient.hSet(`task:${taskId}`, {
status: 'queued',
storage_key: s3Key,
updated_at: Date.now(),
});
// Крок В: Відправляємо легке посилання в RabbitMQ чергу
const message = { taskId, storageKey: s3Key, bucket: BUCKET_NAME };
rabbitChannel.sendToQueue('task_queue', Buffer.from(JSON.stringify(message)), {
persistent: true,
});
res.status(202).json({
success: true,
message: 'Task accepted and offloaded to SeaweedFS',
taskId,
s3Key,
});
} catch (error) {
console.error('Error creating task:', error);
res.status(500).json({ error: error.message });
}
});
// --- 3. ФОНОВИЙ ВОРКЕР (Consumer) ---
async function startWorker() {
console.log(' Worker started, waiting for messages...');
rabbitChannel.consume('task_queue', async (msg) => {
if (!msg) return;
const { taskId, storageKey, bucket } = JSON.parse(msg.content.toString());
console.log(`\n[Worker] Processing task: ${taskId}`);
try {
// 1. Оновлюємо статус у Redis
await redisClient.hSet(`task:${taskId}`, { status: 'processing', updated_at: Date.now() });
// 2. У реальному житті тут можна завантажити файл із SeaweedFS за потреби:
// const getObj = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: storageKey }));
// const bodyString = await getObj.Body.transformToString();
// Імітуємо корисну роботу (наприклад, 1.5 секунди обробки)
await new Promise((resolve) => setTimeout(resolve, 1500));
// 3. Завершуємо задачу, оновлюючи Redis
await redisClient.hSet(`task:${taskId}`, { status: 'completed', updated_at: Date.now() });
console.log(`[Worker] Task ${taskId} successfully completed!`);
// Підтверджуємо обробку повідомлення для RabbitMQ
rabbitChannel.ack(msg);
} catch (err) {
console.error(`[Worker] Failed to process task ${taskId}:`, err);
// У разі помилки повертаємо у чергу або в DLQ
rabbitChannel.nack(msg, false, false);
}
});
}
startWorker();
// Запуск HTTP сервера
const PORT = 3000;
app.listen(PORT, () => {
console.log(` Server running on http://localhost:${PORT}`);
});