×

Боль и страдания перехода на Java 9

Обычно я, как и остальные, перехожу на новую версию явы лишь через полгода-год после релиза. Но в этот раз, вернувшись из 3-недельного отпуска, я был полон сил и решимости стать д’артаньяном пройтись по граблям самостоятельно. Забегая наперед — все это было не зря. Хотя результат получился и не таким хорошим, как я ожидал:

Production instance после перехода на Java 9

Проект у нас довольно простой в плане стека технологий. Мы, например, не используем Spring, Hibernate. А всю модель храним в памяти. Поэтому память — довольно критический для нас ресурс, как и CPU. Текущая нагрузка в 13к req/s каждый месяц становится все больше и больше и иногда не дает мне спать.

Помимо типичных http/s, websockets, mqtt мы также используем собственный полубинарный, полутекстовый протокол на netty. Поэтому идея перехода на 9-ку была оправданной. Так как JEP 254: Compact Strings и JEP 280: Indify String Concatenation.

В общем, вернувшись из отпуска, первым делом я заапдейтил все библиотеки и плагины, которые у нас используются. Список получился таким:

<maven-release-plugin.version>2.5.3</maven-release-plugin.version>
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
<maven-shade-plugin.version>3.1.0</maven-shade-plugin.version>
<maven-surefire-plugin.version>2.20.1</maven-surefire-plugin.version>
<maven-checkstyle-plugin.version>2.17</maven-checkstyle-plugin.version>

<netty.version>4.1.16.Final</netty.version>
<netty.boring.ssl.version>2.0.6.Final</netty.boring.ssl.version>
<log4j2.version>2.9.1</log4j2.version>
<jackson-databind.version>2.9.1</jackson-databind.version>
<disruptor.version>3.3.6</disruptor.version>
<async-http-client.version>2.1.0-alpha25</async-http-client.version>
<twitter4j-core.version>4.0.2</twitter4j-core.version>
<postgresql.version>42.1.4</postgresql.version>
<HikariCP.version>2.7.1</HikariCP.version>
<qrgen.version>2.2.0</qrgen.version>
<bcpg-jdk15on.version>1.57</bcpg-jdk15on.version>
<acme4j-client.version>0.12</acme4j-client.version>
<javaee-api.version>8.0</javaee-api.version>
<javax.mail.version>1.6.0</javax.mail.version>
<javax.activation.version>1.2.0</javax.activation.version>

Это очень важный шаг. Без апдейтов плагинов и либ на новую версию явы лучше не переходить.

Настроил maven-compiler-plugin на девятку:

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-compiler-plugin</artifactId>
   <version>3.7.0</version>
   <configuration>
       <source>9</source>
       <target>9</target>
   </configuration>
</plugin>

И вуаля — все завелось. Единственное, что при запуске JVM вываливалось сообщение в консоль:

WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by io.netty.util.internal.ReflectionUtil (file:/home/azureuser/server-0.28.0-SNAPSHOT.jar) to constructor java.nio.DirectByteBuffer(long,int) WARNING: Please consider reporting this to the maintainers of io.netty.util.internal.ReflectionUtil WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release

Так как часть наших юзеров используют приватные сервера, оставлять это сообщение без внимания было нельзя. Нас засыпали бы вопросами: «Ааа, что это такое?». Сообщение можно было бы пофиксить через опцию JVM. Но это был не вариант, так как лишь часть пользователей выполняют инструкции. Поэтому, недолго думая, я закинул вопрос на СО. И получил в ответ довольно забавный хак:

public static void disableWarning() { 
    try { 
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); 
        Unsafe u = (Unsafe) theUnsafe.get(null);
        Class cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
        Field logger = cls.getDeclaredField("logger");
        u.putObjectVolatile(cls, u.staticFieldOffset(logger), null);
    } catch (Exception e) {
        // ignore
    }
 }

Но он сработал.

Создание module-info.java

Теперь предстояло создать ява-модули (module-info.java) для моих мавен-модулей, чтобы по настоящему мигрировать на 9-ку, а не просто скомпилить под нее весь код.

И тут сразу начались проблемы. Первый же модуль:

module cc.blynk.acme {
    requires acme4j.client;
    requires acme4j.utils;
}

Модуль, который генерит Let’s Encrypt сертификаты, отказывался собираться:

Error:java: the cc.blynk.acme module reads package org.shredzone.acme4j.util from both acme4j.client and acme4j.utils

Ошибка говорит нам, что пакеты с одним и тем же именем подтягиваются из двух разных модулей. Чего делать нельзя. Есть несколько способов решить эту проблему, но все они требуют изменений на стороне самой библиотеки. Поэтому я быстренько забросил тикет. К счастью, от этой части зависел только 1 модуль, поэтому я переключился на создание модулей в других пакетах.

Вторая неприятность ждала меня в модуле отправки писем.

module cc.blynk.server.notifications.mail {
   requires log4j.api;
   requires javaee.api;
   requires java.activation;

   exports cc.blynk.server.notifications.mail;
}

Пакет java.activation оказался устаревшим, и будет удален в следующих релизах явы. Опять вопрос на СО. В общем, пока все можно оставить как есть. Но если не нужны проблемы в будущем, то лучше заменить:

<dependency>
   <groupId>javax.activation</groupId>
   <artifactId>activation</artifactId>
   <version>1.1</version>
<dependency>

на

<dependency>
   <groupId>com.sun.activation</groupId>
   <artifactId>javax.activation</artifactId>
   <version>1.2.0</version>
</dependency>

P. S. Обычно javax.activation пакет идет в зависимостях к

<dependency>
   <groupId>com.sun.mail</groupId>
   <artifactId>javax.mail</artifactId>
</dependency>

Сразу после добавления первого модуля отвалился maven-checkstyle-plugin:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-checkstyle-plugin:2.17:check (default-cli) on project email: Failed during checkstyle configuration: NoViableAltException occurred during the analysis of file /home/xxx/IdeaProjects/blynk-server/server/notifications/email/src/main/java/module-info.java. unexpected token: module 

Оказалось, плагин еще не готов к девятке, поэтому нужно добавить module-info.java в исключения при чекстайле:

<plugin> 
<groupId>org.apache.maven.plugins</groupId> 
<artifactId>maven-checkstyle-plugin</artifactId>
 <configuration> 
   <excludes>**/module-info.java</excludes> 
 </configuration>
</plugin>

Теперь пришел черед модулей, которые зависят от netty. У нас в проекте есть, например, такая зависимость:

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-transport-native-epoll</artifactId>
    <version>${netty.version}</version>
    <classifier>${epoll.os}</classifier>
</dependency>

Для библиотек, которые еще не перешли на девятку, ява автоматически генерирует имя на основе имени jar-файла. Поэтому для jar-файла транспорта выше получилось имя:

netty.transport.native.epoll

Но есть один нюанс: native — зарезервированное слово в яве. Его нельзя использовать в названии пакетов и модулей. Попытка скомпилировать этот модуль:

module core { 
    requires netty.transport.native.epoll;
 }

выдает:

module not found: netty.transport.<error>

Можно также проверить это из командной строки:

jar --file=netty-transport-native-epoll-4.1.16.Final-linux-x86‌​_64.jar --describe-module

выдает:

Unable to derive module descriptor for: netty-transport-native-epoll-4.1.16.Final-linux-x86‌_64.jar netty.transport.native.epoll: Invalid module name: 'native' is not a Java identifier

Тут решение, как и в первом случае, требует изменений на стороне библиотеки. Самым простым вариантом будет добавить предопределенное имя модуля в manifest-файл джарки:

<Automatic-Module-Name>netty.transport.epoll</Automatic-Module-Name>

Pull request c фиксом. Теперь жду фиксы от мейнтейнеров, чтобы закончить модуляризацию нашего проекта.

Еще парочка проблем

После этого, с горем пополам, удалось собрать проект с частичной модуляризацией. Но после релиза оказалось, что Java 9 нет для ARM-архитектур. И судя по всему, в ближайшее время не предвидится. Поэтому пока что собираем 2 артефакта. Это оказался самый простой вариант:

<profile>
   <id>java9</id>
   <activation>
       <jdk>9</jdk>
       <activeByDefault>true</activeByDefault>
   </activation>
   <properties>
       <jarClassifier>java9</jarClassifier>
       <source>9</source>
       <target>9</target>
       <!-- just fake property. means do not exclude anything -->
       <version.specific.exclusions>**/ccxxyy.java</version.specific.exclusions>
   </properties>
</profile>

<profile>
   <id>java8</id>
   <activation>
       <jdk>1.8</jdk>
   </activation>
   <properties>
       <jarClassifier>java8</jarClassifier>
       <source>1.8</source>
       <target>1.8</target>
       <version.specific.exclusions>**/module-info.java</version.specific.exclusions>
   </properties>
</profile>

С наскоку собрать multi-jar не получилось. Поэтому если у кого-то есть готовая конфигурация — буду рад ознакомиться.


На этом, собственно, все. Делитесь своими историями в комментах. Мы уже перевели все наши ~20 серверов на Java 9, хоть и с частичной модуляризацией. А вы?

P. S. Благодаря раннему переходу на девятку мы попали на слайд Марка Рейнхольда (head of Java) на проходящей сейчас JavaOne конференции.

Все про українське ІТ в телеграмі — підписуйтеся на канал DOU

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn

Схожі статті




66 коментарів

Підписатись на коментаріВідписатись від коментарів Коментарі можуть залишати тільки користувачі з підтвердженими акаунтами.

И всё это чтобы понизить heap на 15%, когда у вас over 36% гуляет? Была пичалька. Могли бы честно отложить на февраль.

Ну никто ж не знал каким будет прирост для нашего случая. Во-вторых, это только 1 сервер, на некоторых ситуация гораздо хуже.

На деле вопрос скорее упирается в свободный человеческий ресурс. Если «пан має час та натхнення», то почему бы и нет. Было время я и альфа-версии новых пакетов ставил в продакшен, оказывались на удивление приятнее финалок, зато имели изумительный саппорт от разрабов.

тогда бы на слайд не попали :-)
хотя там все равно ничего рассмотреть невозможно :-)

Один только вопрос — почему Maven, а не Gradle?

Мне в мейвене нравится структурированность. На какой проект не прийди — везде одинаковая структура и легко вникнуть за 5 мин. На гредле у каждого свой зоопарк. Пу сути гредл — тот же ант, только с правильной системой менеджмента зависимостей.

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

тула тулой, а интеграция с intellij до сих пор хромает

Необходимость релоадить проект, сбрасывать кеши, когда в градле кто-то что-то изменил. Бесит.

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

Хм, у меня такое поведение было на мавеновских проектах и это стало еще одним пунктом за переезд на гредл (где я такоего не наблюдал) :)

Галка автоимпорт) хз, страдала вся контора, и проблема скорее всего в идее, но осадочек остался)

В Gradle у каждого проекта тоже может быть одинаковая структура

А может и не быть :). В том то и дело. Мейвен фактически заставляет создавать модули по одному шаблону. В гредле больше свободы. Из-за этого каждый гредл проект со своими приколами в которые нужно вникать. Не везде, конечно, но частенько.

Просто странно использовать тулзу, последняя версия которой вышла в 2010 году.

Last Published: 2017-10-07

Я имею в виду 3.0 вышла в 2010 году. 3.1, 3.2 и прочие — это мелкие фитчи и баг-фиксы. Сравните это с тем количеством фитч, которые добавили в Gradle только за последние два года.

Сравните это с тем количеством фитч, которые добавили в Gradle только за последние два года.

Ну я еще не встречал проблемы (за лет 7 использования), которую не решает мейвен или его плагины.

вышла в 2010 году

Как будто это что-то плохое! Ещё скажите make не использовать.

Вот мейвэновский проект с гитхаба etc взял — и он собрался. Грэйдэл начнёт тебе рассказывать что проект из нескольких частей, собран разными версиями грэйдла, разумеется зависимости проапдейтить забыли — и как эта [плохое слово с мягким знаком] любит — сообщения об ошибках сборки совершенно не отражают суть того что ему действительно не хватило для счастья.

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

Один только вопрос — почему Maven, а не Gradle?

А почему гредл, а не мавен?

Какой смысл задавать подобные вопросы? Каждый использует то что ему удобнее.
Знаю ребят которые все соберали ант+айви, у них была построена целае экосистема вокруг этого и переезжать они не соберались, когда мы последний раз обсуждали с ними подобные вопросы.

Кстати, с G1 вылезли небольшие проблемки, пока нету времени копать закинул вопрос на SO —
stackoverflow.com/...​java-9-g1-work-without-th

CPU consumption increased by 4%

да ладно, ну что такое 4% даже для такой мосчной VM?
памяти же теперь выжрано меньше, вот и приходится скорее всего за это расплачиваться процессорным временем, чаще собирая мусор по heap-у
тем более, что в случае с такой абстракцией как vCPU реально никакого роста нагрузки на 4% может и не быть, проценты от абстракции — это абстракция в квадрате
точные метрики по процессорной утилизации этой VM можно снять только непосредственно с гипервизора

да ладно, ну что такое 4% даже для такой мосчной VM?

4% от 20% это 20%. То есть есть перейти на инстанс у которо 80% — он почти наверняка умрет.

чаще собирая мусор по heap-у

Врятли. Скорее всего это просто бага в Ж1. В пользу этого варианта говорит тот факт, что 6 часов все отлично работало.

6 часов все отлично работало.

мусора еще не успело собраться критическое значение :-)
но из приведенных графиков таки совершенно не очевидно, что нагрузка CPU изменилась именно на Java, а не еще каких-то процессах

Вы на Jazz тоже говорите яз? А на javascript яваскрипт?

Зависит от настроения. Когда пишешь статью — экономишь символы.

Стив Йобс переворачивается в гробу)

Вам реально важно правильное написание терминов, аббревиатур или же содержание статьи?

Видимо помеха ) Судя по количеству Ваших статей )

«Олегь» вам також не буде різати вухо? ;)

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

Джава из-за кофе, а кофе из-за острова, а остров-то Ява! Так что вполне)))

Остров — Ява, язык — Джава, так уж принято. Название языка от английского, острова от индонезийского, отсюда и разные произношения. Остров на латинице вообще Jawa. А так и C# можно До-диезом называть.

Ну, люди холиварят давно на эту тему, но ходят слухи что в Оракле произносят «джава», можно ориентироваться на это. Главное — станешь джава-чемпионом — произноси как хочешь, а до тех пор молча старайся))

хороший playthrough, спасибо

Для тех, кто имеет опыт работы со Scala/Groovy это похоже на клуб любителей Жигулей, которые ездили на восьмерке, а теперь завод выпустил девятку. В то время как Scala это Tesla X

Скале бы научиться лямбды инлайнить еще

Жигулей на дорогах тоже больше, но лучше как автомобиль они не становятся. Просто такую машину проще себе позволить

Ваша ссылка учитывает запросы типа: vitamin c, mercedes c, usb c
А вот так — тренд по языку «C»:
trends.google.com/...​7sbkfb,/m/091hdj,/m/01t6b

Голанг между С и Скалой болтается, однако хайпа больше, чем на тему С)

Это да, особенно не хватает хвостовой рекурсии и нормальных дженериков (мое субъективное мнение).

Scala это Tesla X

Скала это Тесла ХЗ, которую на галстуке тянет всё тот же Жыгуль. Но водителю завязали глаза, чтобы он думал что оно само едет и знает куда.

Scala — отличный язык чтоб писать код. Но ни в коем случае не дебажить и не покрывать тестами. Да и насчёт девятки не понял — что в Scala способно заменить модули и директиву exports/requires? private[package]? Да нет же, это скорее package local. Что тогда?

явы
ява-модули
Боль и страдания

=).

Ну как быстрое решение пойдет. ctrl+c, ctrl+v — 2 минуты. А отвечать потом пользователям займет гораздо больше времени. Вот, например — community.blynk.cc/...​ocal-blynk-server/8880/35

Тем более, что когда-то этот варнинг уберут вместе с доступом к классам, то все будет работать без проблем дальше.

На оффсайте оракла есть только 64-битная версия JDK.

Да. И из комментов, что я видел 32-х битной версии вроде как и не будет.

у Oracle
но они не единственные майнтейнеры Java

в продакшене обычно оракул ждк.
с другими не сталкивался.
подскажете?

openjdk (jdk9) только х64 версия на оффсайте

IBM Java, например (а очень-очень давно и MS Java была)
на IBM-овских RISC-ах и мейнфреймах — с SUN Java как то сразу не сложилось :-)

openjdk (jdk9) только х64 версия на оффсайте

думаю х86 32-битный — никому уже и не нужен, по крайней мере уже давно пора закопать эту стюардессу
а вот для ARM, судя по обсуждению из ссылки ниже

both a 32-bit and a 64-bit implementation
думаю х86 32-битный — никому уже и не нужен, по крайней мере уже давно пора закопать эту стюардессу

Не совсем. Большинство одноплатных PC идут 32-х разрядные. Хотя в последнее время и они переходят на 64.

Большинство одноплатных PC идут 32-х разрядные

и какой из них процент X86?

Не знаю. Думаю что довольно маленький. В основном АРМки.

вот и ответ на вопрос о рыночной доле 32-битного х86
и целесообразности поддержки там новых версий Java

жесткий enterpґise забыли. сколько гигатонн кода...

я в этом самом ынтырпрайзе и работаю
и там обычно немного другая история, оно (х32 приложения) или вообще не про джаву или никуда с какого-нибудь 1.6 и не думает двигаться

ms java была так давно... что даже и не вспомнил )
ibm java на офсайте 9-я версия в статусе бета и для зарегистрированных юзеров.
оракловый sdk практически безальтернативный для бизнеса. рановато хоронить стюардессу по имени х32. думаю дело в том, что оракл просто забил на развитие и не желает тратить ресурсы на две ветки. видимо сольют java под крыло apache foundation, после и будет видно.

подскажете?

HotSpot (Oracle), OpenJDK, Zulu (Azure), OpenJ9 (IBM).

после релиза оказалось, что Java 9 нет для ARM-архитектур. И судя по всему, в ближайшее время не предвидится.

уже год как портируют
mail.openjdk.java.net/...​v/2016-August/000406.html

А для 32-х разрядных вообще не планируют. Насколько я знаю.

Ну написать можно что угодно. А по факту 32 можем так и не увидеть.

В предрелизных билдах была 32 версия OpenJDK 9, после выхода GA доступна только 64 для linux. Если очень сильно заморочится, можно найти версию, люди ее на файлообменники заливали.

Підписатись на коментарі