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
- И конечно же, проект стал быстрее собираться и делоиться
Автор: pull