Релокейт-опрос 2017 (для тех, кто уже уехал). Собрано более 1500 анкет.
×Закрыть

Боль и страдания перехода на 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 конференции.

62 комментария

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

И всё это чтобы понизить 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

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

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

=).

Ну как быстрое решение пойдет. 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-х разрядных вообще не планируют. Насколько я знаю.

both a 32-bit and a 64-bit implementation

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

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