- PVSM.RU - https://www.pvsm.ru -

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

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

Java и без 16Gb памяти? - 1

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

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

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

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

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

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

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

  • compact1 — 10mb
  • compact2 — 12mb

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

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

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

IoC фреймворк

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

База данных

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

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

И с jdbc:

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

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

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

  1. А что, если обхитрить JVM: взять compact1 и вручную подложить java.sql api [12]? Ответ: не получится. В 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 [13] — Ха-ха-ха
  • jetty [14] — не compact1
  • nanohttpd [15] — не servlet, нет поддержки сессий. Но видимо такова судьба Embedded разработчика.

SSL temination

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

Вэб клиент

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

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

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

Логирование

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

Json

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

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

Итого

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

Автор: dernasherbrezon

Источник [23]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/java/260520

Ссылки в тексте:

[1] satnogs: https://satnogs.org

[2] raspberry pi: https://www.raspberrypi.org

[3] Java ME: https://habrahabr.ru/post/210592/

[4] какие-то части rt.jar: http://www.oracle.com/technetwork/java/embedded/resources/tech/compact-profiles-overview-2157132.html

[5] Dagger: https://github.com/google/dagger

[6] Feather: https://github.com/zsoltherpai/feather

[7] Guice: https://github.com/google/guice

[8] guava: https://github.com/google/guava

[9] picocontainer: http://picocontainer.com

[10] berkleydb: http://www.oracle.com/technetwork/database/berkeleydb/overview/index-093405.html

[11] sqlite: https://github.com/xerial/sqlite-jdbc

[12] java.sql api: https://mvnrepository.com/artifact/org.xerial.thirdparty/jdbc-api/1.4

[13] tomcat: https://github.com/apache/tomcat

[14] jetty: http://www.eclipse.org/jetty/

[15] nanohttpd: https://github.com/NanoHttpd/nanohttpd

[16] jtwig: http://jtwig.org

[17] только: https://jira.qos.ch/browse/LOGBACK-1071

[18] gson: https://github.com/google/gson

[19] jacksonxml: https://github.com/FasterXML/jackson-databind

[20] https://github.com/ralfstx/minimal-json: https://github.com/ralfstx/minimal-json

[21] https://stackoverflow.com/questions/13825403/java-how-to-get-logger-to-work-in-shutdown-hook: https://stackoverflow.com/questions/13825403/java-how-to-get-logger-to-work-in-shutdown-hook

[22] https://github.com/dernasherbrezon/r2cloud: https://github.com/dernasherbrezon/r2cloud

[23] Источник: https://habrahabr.ru/post/333430/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best