TLTD
- удалил jar из сборки проекта
- заменил его таском, который быстрее в 7 раз
Детали и результат под катом.
О проекте
Веб сервис на java, который отдает наружу rest api и websockets, а внутри умеет ходить в распределенную бд и распределенный кеш.
Проект использует embedded jetty для старта, запускается через public status void method
.
Доставляется на сервер в виде fat jar и запускается через java -jar myapp.jar app.yaml
Профилируем
Gradle отличный иструмент, который из коробки дает профайлер. Запустим билд с параметром --profile
и подождем результат.
./gradlew clean build --profile
Думаю, результат в комментировании не нуждается:
Изучаем проблему
Первым делом я решил посмотреть как сейчас создается fat jar:
jar {
manifest {
attributes "Main-Class": "com.baeldung.fatjar.Application"
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
Прописываем в манифесте класс с main методом и распаковываем jar dependencies в корневую папку jar архива.
Это можно проверить, если сделать распаковав jar unzip myapp.jar
и посмотреть дерево текущией папки tree .
Неудивительно, что это медленно, много мелких файлов нужно сначало распаковать, а потом обратно запаковать.
Оптимизируем
Далее, я попровал нагуглить более быстрый вариант создания jar файла.
Я попробовал плагины для gradle:
gradle-fatjar-plugin — больше не поддерживается
shadow — удалось собрать им, но он использует тот же способ, как и выше, поэтому это не дало прироста в скорости
gradle-one-jar — вообще не смог запустить, честно скажу, возможно нужно было просто потратить больше времени
Тут мне пришла идея, даже можно сказать вызов. А как запустить приложения без jar? У меня как раз был распакованных архив, для того, чтобы попробовать это.
Оказалось не сложно:
java -cp . com.example.Main app.yml
Проект отлично запустился, подхватил нужный конфиг.
Параметр -cp это classpath, который говорит java процессу, где лежат все классы проекта.
Получается, проект может жить без jar? Воспользовавшись небольшой помощью gradle community, я получил таск, который создает exploded версию jar:
task explodedJar(type: Copy) {
with jar
into "${buildDir}/exploded"
into('lib') {
from configurations.runtimeClasspath
}
}
jar.enabled = false
assemble.dependsOn explodedJar
Таск
- кладет все классы и ресурсы в
exploded
папку - кладет все runtime зависимости в папку lib
- дабавляет explodedJar и исключает jar таск из ./gradle build
Запускаем еще раз
./gradlew build --profile
Наслаждаемся результатом
Думаю, комментарии тут опять не нужны.
Тут возможно еще стоит продублировать гистограмму из начала статьи, но я этого делать не буду.
Но как деплоить?
Чтобы не делать эту статью очень длинной, просто оставлю одну команду для копирования проекта на сервер:
rsync --delete -r build/exploded api.example.com:/opt/myapp
Итог
- Проект стал проще из-за того, что мы убрали из него такую сущность как
jar
- Всегда можно посмотреть, что конкретно попадает в нашу сборку, просто открыв папку
build/exploded
- И конечно же, проект стал быстрее собираться и делоиться
Комментарии (9)
aleksandy
20.03.2018 20:01+1Тут мне пришла идея, даже можно сказать вызов. А как запустить приложения без jar?
Какой вызов? Об этом пишут в любом учебнике по Java.nsiniakevich Автор
20.03.2018 21:09-1В учебниках много всего было, но то, что не используется в реальной жизни быстро забывается. Я очень смутно помнил, как это делается, пришлось потратить несколько минут на гугл и чтения man java.
nsiniakevich Автор
21.03.2018 16:48опа, минусы)
Давайте разбираться, с чем вы не согласны?
Еще раз повторю свою позицию: учебники нужны, но помнить все, что в них написано нереально. Если мне нужна информация, которую я когда-то читал, но помню смутно из-за того, что давно не использовал, то я пользуюсь гуглом.aleksandy
21.03.2018 21:58Так всё помнить и не надо. Но как запускать программу в консоли без IDE — это основа основ.
Любой HelloWorld в любом источнике состоит плюс-минус из одних и тех же шагов:
- Создать файл HelloWorld.java.
- Описать в нём класс HelloWorld с main-методом.
- Скомпилировать, выполнив javac HelloWorld.java
- Запустить, выполнив java HelloWorld
И каждый шаг расписывается «что», «как» и «почему».
Мне на ум приходит единственный вариант, при котором это знание было Вами утеряно: Вы не написали ни одной программы, не используя IDE, maven/gradle/ant/etc. Я не говорю о чём-то масштабном, хотя бы банальный сумматор двух аргументов командной строки. С целью пощупать и понять, что же творится под капотом любой IDE.nsiniakevich Автор
21.03.2018 23:29Все верно. Я использую intellij idea и сборщики проектов всегда, в том числе и на домашних проектах. Ну бывает еще atom или textmate.
Да, когда я учил джаву, я компилировал и запускал в консоле. Но сейчас у меня нет такой необходимости и эти знания потихоньку теряются.
axmetishe
20.03.2018 22:44Блин, круто, а можно вообще сырцы кидать на хост и компилить там…
*сарказм*bores
21.03.2018 09:30+3Я видал как томкату класс-файлы подкладывали. А потом через несколько лет оказалось, что это всё работает, только в двух местах: в IDE разраба и на проде. Даже пересобрать не удалось.
Так что ваш способ ещё не самый юморной :)axmetishe
21.03.2018 22:35Хы… я тоже такое видел, только не в развернутую варку, а в джарку, упакованную в варку — библиотеку — классик впиливали =)))
Вообще, меня всегда удивляет, когда берут достаточно жирную тулу, читать как комбайн, а затем для ускорения сборки отключают zip — это как купить автомобиль для повседневной езды, но для снижения времени разгона выкинуть сиденья или для повышения устойчивости сделать жесткую подвеску в ущерб комфорту.
Мое мнение, если хочется сделать эту сборку быстрой, нужно убрать оверхед удобства.
Как-то на тренинге я сравнивал время сборки примитивной варки тулами Ant+Ivy, Maven, Gradle. Скорость сборки чистого прогона, но с прогретым кэшем зависимостей, была 3 сек, 7 сек, 19 сек соответственно.
Я более чем уверен, что если написать майк файл, варка соберется гораздо быстрее.
Либо шашечки, либо ехать.
UbuRus
build.gradle:
terminal: