Мессенджер на mikrotik Chr и java: как запустить сервер в контейнере routeros

Домашний "слабосолёный" мессенджер на MikroTik CHR и Java

Почему в маршрутизаторах до сих пор нет встроенного мессенджера - хотя бы самого простого, текстового? Роутер ведь по сути и есть устройство, которое "доставляет сообщения" от одного узла к другому. Производители уже научились встраивать в сетевое железо всё подряд - от сложной аналитики трафика до модных "умных" функций, - а вот элементарного личного чата в комплекте почему-то не встречается. В какой-то момент стало любопытно: а если собрать такой мессенджер самому, сможет ли он работать прямо на RouterOS?

Вместо экспериментов с настольным hAP AC3 LTE6 на этот раз я взял MikroTik CHR на VPS и немного погонял идею на hAP_AX2. Конфигурация CHR получилась весьма аскетичной: одно ядро 2200 MHz, 512 МБ оперативной памяти и 16 ГБ диска. Лицензия - CHR P1, но для текстовой переписки теоретически хватило бы и бесплатного варианта, даже с его ограничением скорости портов до 1 Мбит/с.

Архитектура: один проект, два мира - клиент и сервер

Чтобы не распыляться, всё собрал "в одном казане" в Android Studio: клиентское приложение и серверная часть разнесены по модулям, но живут в одном проекте. Серверный модуль называется Limserver - по сути, это компактный HTTPS‑сервер плюс SQL‑база, подключённая через HikariCP.

По серверу всё довольно прямолинейно:

- база данных с 4 таблицами;
- пул на 8 рабочих потоков;
- 8 подключений к базе данных.

При этом параметры не "зашиты намертво": через `server.properties` можно поменять количество потоков и коннектов к БД, а заодно задать квоты по дисковому пространству для каталога `/media`. Именно туда складываются файлы, которые пользователи пересылают друг другу.

Сам по себе серверный модуль получился небольшим - около 14 МБ. Но есть нюанс: RouterOS‑контейнеру для запуска Java‑приложения нужна среда выполнения.

Как уместить Java в роутер: кастомный JRE вместо "полножирного"

Готовый образ с JRE легко переваливает за 150 МБ, что для роутерного сценария уже жирновато. Поэтому сборка делалась в два этапа:

1) в JDK‑образе `eclipse-temurin:21-jdk-alpine` собирается минимальный кастомный JRE только с нужными модулями;
2) затем этот рантайм пакуется вместе с JAR в Alpine Linux.

Обязательная деталь - таймауты для HTTPS‑сервера. Я поставил 3 минуты: на слабом CHR при передаче файлов он иногда "задумывается", и без нормальных таймаутов можно получить странные обрывы и подвисания. Итоговый контейнер в таком виде вышел примерно на 71 МБ - уже реально для жизни.

Развёртывание на CHR: каталоги, база, сертификат

Дальше - "залить кипятком" контейнер на CHR и подготовить две рабочие папки:

- `/db` - база данных, сертификат и настройки `server.properties`;
- `/media` - временное хранилище пересылаемых файлов.

Так как HTTPS поднимается внутри контейнера, нужен сертификат. Проще всего сделать самоподписной - при желании его можно импортировать и в клиентское приложение, чтобы не ругалось на доверие.

Контейнер я запускал в условиях жёсткого лимита памяти: `Memory High` - 128 МБ, `Memory Max` - 144 МБ. Поставил бы больше, но "больше" в этой конфигурации просто неоткуда взять.

Функции мессенджера: без групп, но с файлами и статусами

Философия проекта - "простое, но рабочее". Поэтому:

- чаты только тет‑а‑тет, без групп;
- отправка файлов любых форматов до 50 МБ, включая голосовые сообщения;
- уведомления;
- онлайн‑статусы собеседников.

По хранению данных сервер устроен аккуратно: чувствительные вещи в базе лежат в зашифрованном виде - сообщения, файлы, приватные ключи пользователей (они нужны для синхронизации), а также хэши паролей для авторизации. В открытом виде остаются логины, отображаемые имена и публичные ключи - то, что скрывать смысла нет.

Нет внешних сервисов - значит, есть компромисс по доставке

Так как никаких пуш‑сервисов и "облачных помощников" не используется, приложение в фоне не может получать новые сообщения моментально. Когда клиент на телефоне не активен, сервер опрашивается Worker'ом раз в 15 минут - чаще делать бессмысленно, да и батарейку жалко. В активном режиме, естественно, всё работает быстрее.

Важное ограничение: Limserver должен быть доступен по IP из локального диапазона, чтобы клиент мог к нему подключиться в вашей сети. При желании на CHR можно дополнительно "зажать" IP контейнера правилами и разрешить доступ только из нужных подсетей - это полезно, если вы не хотите, чтобы сервис вообще видел кто-то лишний.

Производительность: текст летает, большие файлы - уже испытание

С текстом и фотографиями проблем практически не заметно. А вот крупные файлы на такой памяти и в контейнере RouterOS идут тяжело - и дело не только в скорости сети. Узкое место - память и кеширование.

Чтобы не превращать отправку одного видео в "блокировку всего", исходящие сообщения я разделил по трём потокам - в зависимости от `id` чата. Идея простая: если в одном диалоге улетает тяжёлый файл, в другом можно продолжать переписку без ощущения, что приложение "встало".

Загрузку тоже пришлось разделять: тексты подтягиваются в одном потоке, а файлы - отдельно, своим потоком. Это заметно улучшает ощущение отзывчивости.

Главная борьба - с кэшем: почему приходится чистить вручную

Самая неприятная часть эксперимента - page cache. На сервере после каждого принятого или отправленного файла приходится чистить cache page, потому что сам он нормально "отлипает" только когда файл удаляется (а удаляется он уже после успешной доставки получателю). Если этого не делать, при моих лимитах памяти контейнер после второго файла размером 35+ МБ почти полностью забивается кэшем - и скорость загрузки/выгрузки резко падает.

И это не теория, а практика: в стресс‑тесте я гонял одновременно между телефоном и планшетом по 10 файлов размером по 35 МБ. Оба устройства одновременно и заливали, и скачивали. В итоге около 700 МБ трафика прошли через контейнер, у которого оставалось примерно 50 МБ свободной памяти, примерно за 30 минут. Выжило - но назвать это быстрым нельзя.

Как не захламлять CHR: политика удаления файлов

Чтобы CHR не превращался в мусорку из чужих вложений, файлы удаляются:

- сразу после первого подтверждённого скачивания получателем;
- либо через месяц, если получатель так и не скачал.

Есть мысль уменьшить срок до недели - месяц в домашнем сценарии иногда избыточен.

Но у такого подхода есть важный побочный эффект: если один аккаунт используется на нескольких устройствах, файл фактически "одноразовый" - кто первым скачал, тот и получил, а остальным уже может не достаться, потому что вложение будет удалено после подтверждения.

Дополнительные улучшения и практические советы (добавлено)

Отдельно стоит продумать лимиты на `/media`: даже при автоматическом удалении файлов легко попасть в ситуацию, когда несколько больших отправок подряд съедают всё место. Квоты в конфиге - не декоративная опция, а способ не положить систему внезапным заполнением диска.

Если планируется эксплуатация не "для эксперимента", полезно сразу решить вопрос с логированием. На роутере лог должен быть информативным, но не прожорливым: слишком подробные логи быстро раздуваются, а хранить их негде. Оптимально - ротация и ограничение по размеру.

Ещё одна вещь, которая быстро всплывает в быту - время на устройстве. Для HTTPS и корректной работы сессий часы на CHR должны быть выставлены нормально, иначе можно получить странные ошибки соединения и проблемы с сертификатами.

Также стоит заранее определить, как именно вы будете подключаться к Limserver: только из домашней сети или ещё и через VPN. Второй вариант часто удобнее и безопаснее, чем открывать доступ "наружу", даже если технически очень хочется.

С точки зрения клиента полезно иметь режим "экономии трафика": не автокачать медиа, пока пользователь не откроет чат, или ограничить размер авто‑загрузок. На слабом сервере это снижает пики нагрузки, а на телефоне экономит батарею.

Наконец, интерфейс. Я не делал ставку на дизайн - добавил лишь четыре варианта цвета сообщений собеседника на выбор, чтобы диалоги хоть немного отличались. Но даже минимальные мелочи вроде аккуратной индикации загрузки файла и понятных ошибок передачи заметно улучшают впечатление, особенно когда сервер работает на грани ресурсов.

Вывод: мессенджер на роутере возможен - но с оговорками

Собрать домашний мессенджер на MikroTik CHR реально: текст, фото, статусы, уведомления и даже файлы до 50 МБ - всё это живёт на очень скромном железе и в контейнере RouterOS. Но за компактность приходится платить: медленная передача больших вложений, необходимость контролировать кэш и аккуратно обращаться с памятью, компромиссы по доставке сообщений в фоне.

Зато получается честная автономная система без внешних сервисов: вы сами контролируете сервер, хранение и доступ, а роутер внезапно превращается не только в "маршрутизатор", но и в домашний узел обмена сообщениями - как будто так и должно быть.

Прокрутить вверх