Java и без 16Gb памяти? +34


Однажды меня посетила мысль о том, что надо закодить что-нибудь на Java для RaspberryPI. Предыстория того, как я дошёл до жизни такой, сама по себе потянет на отдельный пост. Но вот сочные технические подробности, трудности и счастливый конец ниже под катом.





Постановка задачи



Немного разочаровавшись в движении проекта satnogs, я решил попробовать сам написать базовую станцию для приёма радио сигналов на raspberry pi. Проанализировав текущую функциональность satnogs и сложив с собственным заскорузлым enterprise пониманием того, что такое стабильная платформа, я придумал следующие требования:

  • java вместо python. Конечно же.
  • низкое потребление ресурсов. Embedded же.
  • переиспользование уже существующих библиотек. Цель проекта не научиться декодировать самому, а максимально интегрировать уже существующие библиотеки
  • стабильность. Коробочка должна работать сама по себе как можно дольше. В идеале её нужно настроить и забыть.


В результате в противоречие вступают только два требования: Java и низкое потребление ресурсов.

В этот момент я почему то вспомнил древний древний слоган «Java — write once, run everywhere» и присказку, что Java может запускаться на кофеварке. С этого момента началось погружение в Java Embedded.

Если вкратце, то в Java существуют две платформы для написания под маленькие устройства: Java ME и Java Embedded. Первая платформа предназначена для совсем маленьких (кофеварки) устройств, а вторая для тех, что чуть-чуть покрупнее. Я выбрал Java Embedded.

Сама Java Embedded в Java 8 претерпела изменения. Теперь её можно собрать с различными профайлами: compact1, compact2, compact3. По сути, это depedency management для бедных. Каждый профайл содержит какие-то части rt.jar, тем самым уменьшая минимальное потребление памяти JVM при загрузке. На моих как-бы тестах (колонка %RES в выводе команды top), я получил следующее потребление:

  • compact1 — 10mb
  • compact2 — 12mb


Для начала я выбрал самый хардкорный вариант: compact1. Нo если не получится найти под него библиотеки, то можно попробовать compact2.

После выбора версии Java нужно выбрать библиотеки. И вот тут дикий-дикий запад. Поскольку в Java мире всё течёт неспеша и с оглядкой на обратную совместимость, то никто из разработчиков библиотек не побежал оптимизировать свой код под новые профайлы. Тем более скоро выходит Java 9, где всё может ещё раз измениться.

Дальше я проанализировал, минимальный набор библиотек для создания не слишком нагруженного web приложения.

IoC фреймворк


  • Dagger, Feather — нет @PreDestroy, @PostConstruct и принципиально не планируется. Про graceful shutdown разработчики видимо не слышали. Вручную контролировать последовательность вызова метода start, чтобы при остановке в обратном порядке вызвать stop, совсем не хочется делать.
  • Guice — зависимость на guava, а значит ещё +2mb.
  • picocontainer — не compact1


База данных



Какой же Java проект без базы данных. Но тут есть один подвох: в compact1 нет java.sql api. Поэтому я первым делом посмотрел на базы с native api без jdbc:

  • berkleydb. NoSQL, но почему-то зависит от javax.transactional.


И с jdbc:

  • sqlite — библиотека весит 5mb. Видимо содержит все нативные библиотеки для всех платформ.
  • java db. Весит конечно много и разные версии отличаются существенно: 10.8 — 2.5mb, 10.13 — 3.1mb.


Есть ещё куча других мелких непонятных embedded баз данных, которые можно было бы попробовать. Но отлавливать их баги под raspberry pi у меня желания нет.

Зато есть пара других идей:

  1. А что, если обхитрить JVM: взять compact1 и вручную подложить java.sql api? Ответ: не получится. В Classloader есть вот такой замечательный код:

           if ((name != null) && name.startsWith("java.")) {
                throw new SecurityException
                    ("Prohibited package name: " +
                     name.substring(0, name.lastIndexOf('.')));
            }
    


    Вообще непонятно почему существует такой maven артефакт, если его даже теоретически нельзя загрузить.
  2. А может без базы? Для моих целей вполне подходят обычные файлы. Sql join тоже вроде не имеет смысла делать.


В общем отказался совсем от базы. Посмотрим надолго ли.

Web container


  • tomcat — Ха-ха-ха
  • jetty — не compact1
  • nanohttpd — не servlet, нет поддержки сессий. Но видимо такова судьба Embedded разработчика.


SSL temination


  • nginx. 3mb master node + 3mb 1 client worker. = 6mb. Вроде неплохо.


Вэб клиент


  • angular, reactjs — на ровном месте привносят десяток короткоживущих технологий.
  • good-o-templates — наш выбор же.


Шаблонизаторы


  • JSP — слишком тяжело и нужно много библиотек. Даже не стал копать.
  • Freemarker — легко, но как оказалось не compact1.
  • Кто-нибудь слышал про jtwig? Я тоже нет, но они умееют работать в compact1 и поддерживают базовые фичи.


Логирование


  • logback — только compact3
  • log4j — full JRE
  • java.util.logging? — Хуже уже не будет.


Json


  • gson. Зависимость на java.sql (!!!)
  • jacksonxml. Зависимость на org.w3c.dom.Node
  • очередной «нагуглил-ночью» код https://github.com/ralfstx/minimal-json. Посмотрел, вроде там нечему ломаться.


После нескольких запусков и сборке всего вместе выплыло несколько косяков, но их можно поправить конфигурацией. Например:
https://stackoverflow.com/questions/13825403/java-how-to-get-logger-to-work-in-shutdown-hook

Итого


  • все библиотеки в сборе + прогретый кэш для шаблонизатора занимают в памяти ~23mb
  • код открыт и доступен: https://github.com/dernasherbrezon/r2cloud (надеюсь пароли нигде там не закоммитил)
-->


К сожалению, не доступен сервер mySQL