Pull to refresh

Comments 40

Дайте попробую угадать — у вас нет своего репозитория в вашей сети?

Почти. У нас удалённый офис, репозиторий есть в основном офисе. Но вы утрируете, т.к. оффлайновая сборка решила мою проблему лишь отчасти.

Ну может и утрирую, но я такого никогда не наблюдал, имея всегда nexus рядом.


Могу в принципе себе представить проект, собирающийся 25 минут — например весь karaf или camel, может быть. Только смысла собирать его весь не вижу никакого. И делать проект такого размера, не разбитый на модули — тоже.


takari видели? Пробовали?

Проект, который я собирал, по размерам должен быть сопоставим с karaf или camel, и в нём как раз много модулей. Он отчасти есть в opensource, но по NDA я не думаю, что могу публично назвать его.
Насчёт смысла собирать и пересобирать такой большой проект — если вы ещё раз пробежите текст статьи, там как раз и упоминается возможность не собирать постоянно все модули. Надеюсь, кому-то эта возможность пригодится помимо меня, поэтому я и написал об этом.
Про takari — спасибо за наводку!

Ну если он попоставимого с karaf размера — тогда да, понять 25 минут можно. Тогда вам скорее всего еще поможет SSD, а вовсе не i7.


Возможность же не собирать каждый раз все — ну это самый очевидный вариант, на самом деле. Иначе зачем вообще модули?

Ну… Тюнить мавен это конечно хорошо, но как на счет понять причины медленной сборки? Помню у нас билд шел 10 мин, при этом поднятие спринговых контекстов в тестах занимало 6 мин.
+ Отрефакторить код и избавиться от лишнего мусора, которого, я уверен, в проекте хватает.
+ Сборку можно вынести на отдельную мощную машину и собирать там.
Как я уже писал, изначально это огромный opensource проект, в котором основная причина медленной сборки — его размеры.
Рефакторинг кода — это, конечно, хорошая тема для этого проекта, но такая статья будет совершенно не интересна для аудитории Хабра.
При этом описанная ситуация не является уникальной, поэтому мой опыт может пригодиться другим.
Совсем недавно(месяц назад где то) применил абсолютно все ваши наработки на своем «огромном opensource проекте», а то такая жесть получалась. Кстати, где то писали про локальную репку для mvn. Что то мне локальная репа не помогала, mvn не хотел тянуть быстрее чем в 100 кб\с и такое ощущение что записывал файлы и делал свои махинации во мноооооооого раз дольше нежели закачивал.
Говорят, мавен тяжело справляется с реально большими проектами.
Я могу ошибаться, но насколько я понимал год назад (это то время, в течение которого я не пишу на Java), то одна и та же dependency может тянуться множество раз разными библиотеками. Например, какой-нибудь log4j может много раз тянуться разными либами. Мы это проследили, используя dependency:tree и что-то вроде этого. Затем мы довольно кропотливо и муторно добавляли секции exclude для многих зависимостей. В итоге сборка стала значительно легче, но pom.xml стал значительно запутаннее. Я не считаю это хорошим решением, так что в будущем, если бы мне вдруг еще раз пришлось поработать на большом проекте, я бы посмотрел в сторону gradle. Скажу сразу, я последним не пользовался и не хочу сказать, будто он лучше в этом аспекте.
Как-то очень странно. В том что разные библиотеки используют одну зависимость проблемы нет совсем.
Если используемые библиотеки — release, то выкачал maven все либы один раз в .m2 (при первом билде проекта) и всё, дальше локально подтягивает. А если хочется ускорить shapshot-ы, то можно updatePolicy в daily выставить.
Если же проблема в jar hell, когда в зависимостях куча разных версий одной библиотеки, а самостоятельно подбирать неконфликтные версии библиотек не хочется, то можно поглядеть в сторону Spring Boot.
Обосновать бы еще заказчику, почему он должен заплатить за то, чтобы рабочий и везде используемый модуль переписали на Spring Boot. Особенно, когда на стороне заказчика есть свои разработчики, которые изначально этот модуль писали и тоже не видят смысла в том, чтобы вносить в него кардинальные изменения.
Артём, добавлю свои 5c. Такие задачи, как использование Spring Boot, обосновывать заказчику нет смысла и необходимости, а при этом при грамотном ведении проекта остаются области, которые можно заполнять такими задачами. Старайтесь логически вырулить в эту сторону в будущем.
Я согласен с этим, просто мне тяжело в двух словах описать, почему наш проект был настолько неповоротливым. Но в этом есть смысл, да.

А с чего вы взяли, что gradle вдруг будет собирать быстрее?

А вы с чего взяли, будто я так считаю? =) Я же сказал, что не хочу ничего сказать по этому поводу. Это скорее было отступление, что лично мне хотелось бы посмотреть, как gradle поведет себя в схожей ситуации.

Ну вы как-то неопределенно сформулировали, прямо скажем.


Говорят, мавен тяжело справляется с реально большими проектами.

Это, скажем прямо, не доказано. Я знаю как достаточно большие проекты, собирающиеся мавеном, так и достаточно большие, собирающиеся gradle. И я бы сказал, что скорость сборки в основном определяется правильной архитектурой разбиения на модули. Чтобы тривиально не собирать то, что не требуется.


В качестве примера — я видел десятки проектов, где используется wsdl2java (Axis, или CXF). И к сожалению во многих из них plugin генерации java классов по wsdl вызывается при каждой сборке. И является частью проекта, который содержит как генерируемый код, так и самописный. При том, что wsdl является внешним интерфейсом, и меняется скажем раз в год. Это лечится только применением головы и разбиением на более мелкие части. И кстати, практику один pom-> один артефакт придумали совсем не дураки.

здесь достаточно большой проект, если отключить сборку документации, то занимает 3 минуты, притом на достаточно слабой виртуалке, все дело лишь в правильном приложении мавена
github.com/vporoxnenko/mbsa
Если разные версии одной библиотеки тянутся различными транзитивными зависимостями, то часто удобнее бывает один раз прописать версию этой библиотеки в секции dependencyManagement в корневом pom, чем расставлять везде exclude.
А что делать с ситуацией, когда сторонняя библиотека тянет другую версию? Вопрос без подвоха, просто интересуюсь, прокатит ли это в такой ситуации
Да, я именно про эту ситуацию и говорю. Когда вы прописываете версию библиотеки в dependencyManagement, она перекрывает все остальные определения этой библиотеки из всех зависимостей в дереве, включая сторонние.

Рискуя спровоцировать холивар, всё же спрошу. Как Вы считатете, зачем упоминаемые в статье "многие" могли бы собирать локально проект как mvn clean install? Из Вашего опыта, чем они это мотивируют?

На мой взгляд, в пропуске тестов вот совсем ничего спорного нет. Ведём TDD-разработку – компилируем и запускаем конкретный тест. Запушили ветку – CI-сервер принудительно прогнал новый тест и все старые. А то каждый раз при сборке интеграционные тесты гонять терпения не напасёшься.
Тут уже упомянули takari. Для maven 3.3.9 можно в корне проекта разместить каталог .mvn со следующими файлами
extensions.xml
maven.config
timing.properties
jvm.config

extensions.xml — служит для подключения различных расширений, например takari. В моём случае это:
io.takari.maven
takari-smart-builder
0.4.1


io.takari.aether
takari-local-repository
0.11.2


maven.config — параметры мавена для данного проекта:
-T8
--builder
smart

timing.properties — пустой файл, takari использует его для построения оптимального прожода по модулям проекта.

jvm.config — параметры jvm специфичные для данного проекта:
-Xmx4g
-Djava.awt.headless=true
Да, теги из комментария порезались — был XML, стал набор строк. Но минимально мавен координаты расширений остались, а пример extensions.xml каждый может найти сам.
насколько я понял из текста, у вас не сборка медленно выполнялась, а множество тестов. Пробовали «без оптимизаций» просто без тестов выполнить «clean package» — сколько времени на это уходит?
Спасибо за комментарий, это близко к тому, что было на практике. Только я решил добавить ещё «оптимизаций» после того, как в целом удовлетворительный результат уже был достигнут. Порядка двух минут на билд на неплохом железе меня раздражало.
С оптимизациями результат лучше в 5 раз, чем без них, без — порядка двух минут.
На моей практике обычно даже не приходится делать полную сборку на девелоперской машине, т.е. это редкая операция скоростью которой можно пренебречь в пользу полной уверенности что билд проходит от и до.
Полный же билд идет на CI серверах и задействовать инкрементальную сборку там сложно, поскольку разработка ведется в нескольких бранчах одновременно, и билд-серверам приходится переключаться между ними по мере коммитов.

Вот что давало кратный прирост на интеграционных тестах — так это параллельный их запуск (maven-failsafe-plugin), но пришлось повозиться — настроить конфигурации таким обрзом чтобы не было пересечений ресурсов (разные БД, разные exchnage в RabbitMQ, разные listen ports у компонентов, и т.д.). По идее уже должны существовать альернативные пути, например основанные на контейнеризации (думаю под Gradle точно есть), будет здорово если кто-нибудь просвятит.

ЭххЪ, а мне на одном из проектов приходится отключать инкрементальную компиляцию (configuration/useIncrementalCompilation в настройках maven-compiler-plugin) иначе javac падает также как в JDK-8037484.

Также сталкивался. Можно создать для этого профиль в ~/.m2/settings.xml

  <profiles>
  ...
      <profile>
          <!-- http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8067747 -->
          <id>NoIncrementalCompilation</id>
          <activation>
              <activeByDefault>true</activeByDefault>
          </activation>
          <properties>
              <maven.compiler.useIncrementalCompilation>false</maven.compiler.useIncrementalCompilation>
          </properties>
      </profile>
  </profiles>

Мне было проще в build/pluginManagement добавить настройки, проблема только в рамках тех проектов, которые используют кодогенерацию (например, lombok, immutables).


Кроме того, подход с maven settings, что они автоматически не окажутся на машинах других разработчиков и ci/cd сервере.

в Maven c версии 3.3.1 можно настройки и в проект класть (например в ${maven.projectBasedir}/.mvn/settings.xml) — просто потом в ${maven.projectBasedir}/.mvn/maven.config прописываете путь до вашего settings.xml как --global-settings .mvn/settings.xml

Оно при этом будет мержить их с ~/.m2/settings.xml? Если нет, то в моём случае это бесполезно, т. к. коммитить в репозиторий те же пароли от nexus'а нет никакого желания.

будут — ~/.m2/settings.xml это же user-settings.
--global-settings переопределяет ${maven.home}/conf/settings.xml, который редко кто редактирует. При этом даёт пониженный приоритет, т.к. читается она до пользовательского settings.xml.

Понятно, спасибо. Буду иметь ввиду этот вариант.

За других говорить не буду, но моё личное решение касательно сборки с Maven — не использовать для сборки Maven, по крайней мере в dev-окружении. Вместо этого предпочитаю собирать средой разработки Idea. Причина, если кратко — у среды разработки есть контроль версий файлов, который она и использует для инкрементальной сборки. Результат — сборка длится считанные секунды независимо от размера проекта, длительность зависит только от количества изменений, внесённых после предыдущей сборки. Да, Maven отлично справляется с конфигурацией проекта, и именно его файлы конфигурации я использую для генерации артефактов в среде разработки. Но как только все нужные зависимости подключены, я про Maven забываю…
У идеи много своих проблем, на которые сильно влияют размеры проекта, но даже когда все работает именно так, как должно, есть проекты, которые используют кодогенерацию и/или другого рода костыли, на которые нет поддержки в IDE. И чем больше проект, тем больше костылей в процессе сборки=)
Sign up to leave a comment.

Articles