Se um projeto utiliza Maven para construir seus artefatos, é importante que ele mantenha o seu próprio repositório de artefatos. Ter um repositório disponível para um projeto, ou um conjunto de projetos, é fundamental por uma série de motivos:
- O repositório será o local onde os snapshots dos artefatos serão guardados e recuperados pelos diversos membros da equipe;
- O repositório servirá como cache dos artefatos externos, evitando que se acesse sempre repositórios muito utilizados como Maven Central;
- É possível criar proxies para os repositórios externos, de forma que os desenvolvedores utilizem sempre o mesmo repositório, sem ter que se preocupar em mudar a configuração quando um novo repositório externo é incluído;
- O repositório pode ser protegido por um controle de acesso, evitando que pessoas não autorizadas tenham acesso a artefatos que não são públicos.
A importância de manter um repositório de artefatos interno é bem conhecida por aqueles que usam o Maven seriamente em seus projetos. O que muitos não sabem é que a forma como este repositório é configurado pode ter um impacto significativo no tempo de build.
O Nexus é um dos melhores e mais confiáveis repositórios de artefatos Maven disponível. Além de ser Open Source, o Nexus possui uma boa documentação, apresenta um bom nível de segurança e controle de acesso, e armazena os artefatos no sistema de arquivos, o que elimina muitas dores de cabeça. A configuração de um novo repositório Nexus é relativamente simples e mantendo as configurações default, já se consegue ter um repositório bastante eficiente por um bom tempo.
À medida que o projeto cresce e centenas de novos artefatos são incluídos é comum notar que o tempo de build aumenta consideravelmente e então é hora de fazer algumas configurações extras para melhorar esse tempo.
Antes de mais nada, é importante conhecer um pouco o mecanismo usado pelo Nexus para encontrar os artefatos.
Para que todos os desenvolvedores da equipe possam usar a mesma URL para o repositório de artefatos e assim não precisem se preocupar quando um novo repositório externo passa a ser usado, é recomendado que se utilize um "Repository Group". Um Repository Group nada mais é do que uma coleção de repositórios. Esta coleção possui uma URL que representa uma espécie de repositório virtual e é esta URL que é utilizada pelos desenvolvedores da equipe. O próprio Nexus já vem com dois grupos pré-configurados, o "Public Repositories" e o "Public Snapshot Repositories", como mostra a tela da figura 1, retirada de uma configuração padrão do Nexus.
Figura 1 - Grupo do Nexus e os repositórios que fazem parte dele
Note que na Figura 1, o grupo "Public Repositories" é composto de uma série de repositórios externos: Maven Central, 3rd party, java.net - Maven 2, e outros. Este grupo também possui uma URL que é indicada na coluna "Repository path" e é esta URL que será utilizada pelos desenvolvedores na configuração dos repositórios no settings.xml ou no pom.xml do projeto.
Quando o Maven executa uma build, ele irá pedir ao Nexus um determinado artefato. Ao receber a requisição, o Nexus irá procurar pelo artefato nos repositórios do grupo, na ordem em que eles foram configurados, até que ele seja encontrado. No exemplo anterior, os artefatos seriam procurados primeiro no Maven Central, depois no 3rd party e assim sucessivamente. O fato de o Maven Central ser o primeiro da lista é, em geral, uma boa configuração. Como este repositório contém grande parte das bibliotecas open source utilizadas nos projetos Java, é muito provável que o artefato que está sendo procurado seja encontrado lá.
Por tudo isso, uma boa regra para configurar a ordem dos repositórios no seu grupo é colocar os repositórios que contêm o maior número de artefatos que você utiliza no topo da lista. Porém, isso também tem um efeito colateral indesejável. Isso fará com que os seus artefatos internos, aqueles que você está desenvolvendo e que não estão no repositório externo, sejam também procurados no repositório Maven Central. Há dois problemas com isso:
- Uma requisição dos seus artefatos internos ao Maven Central, além de inútil, pode representar problemas de segurança, uma vez que você está expondo informações sobre seus artefatos sob a forma de URLs para um repositório externo;
- Dezenas ou centenas de requisições inúteis estão sendo feitas para um repositório externo, consumindo tempo de build.
Uma solução para este problema é usar um outro recurso do Nexus: o roteamento (ou Routing). Usando este recurso, é possível informar ao Nexus em que repositórios ele deve procurar um determinado artefato. Assim, quando este artefato for solicitado, o Nexus ativará as regras de roteamento e irá procurar apenas nos repositórios indicados pelas regras.
Para demonstrar o impacto que o roteamento pode ter em uma build, o seguinte teste foi realizado:
- Um repositório Nexus foi criado com um grupo composto de diversos repositórios externos, organizando a ordem dos repositórios conforme a regra de maior repositório no topo da lista;
- Foi configurado um build da ferramenta open source Hudson, um projeto grande, que utiliza Maven na construção de seus artefatos;
- Diversas builds e snapshots dos artefatos do Hudson foram enviados ao repositório, deixando o Nexus com uma quantidade significativa de bibliotecas.
Mesmo com a ordem correta dos repositórios nos grupos, o tempo de build de um dos módulos do Hudson usado neste teste foi de 18 minutos e 42 segundos.
Como todos os artefatos que representam snapshots do Hudson deveriam ser encontrados no repositório interno do Nexus, é possível definir uma regra para que todos os snapshots do Hudson sejam procurados apenas no repositório Snapshots. A definição desta regra pode ser vista na figura 2.
Figura 2: Uma regra de roteamento no Nexus
Como mostra a figura 2, a regra de roteamento é composta das seguintes informações:
- URL Pattern: contém uma expressão regular indicando que artefatos devem disparar esta regra. No exemplo, a expressão regular utilizada foi .*/org/jvnet/hudson/.*-SNAPSHOT/.*, o que signfica que todos os artefatos cujo identificador tenha org/jvnet/hudson/ e -SNAPSHOT farão parte desta regra. Caso se quisesse configurar uma regra para todos os artefatos do Hudson e não só os snapshots, a expressão regurar poderia ser apenas .*/org/jvnet/hudson/.*
- Rule Type: a regra pode ser de três tipos: Inclusive, Exclusive e Blocking. Inclusive significa que os artefatos relativos àquela URL Pattern só serão encontrados nos repositórios informados nesta regra. Exclusive significa que os artefatos relativos àquela URL Pattern não devem ser procurados nos repositórios que fazem parte da regra. Blocking significa que os artefatos relativos àquela URL Pattern nunca podem ser encontrados no grupo de repositórios que fazem parte da regra, mesmo que eles estejam lá. O Blocking em geral é usado para evitar que certos artefatos sejam acessados por um determinado grupo. Neste exemplo, a regra é do tipo Inclusive.
- Repository Group: permite selecionar que grupos de repositórios utilizam a regra.
- Ordered Routed Repositories: indica quais são os repositórios do grupo em que se deve procurar pelo artefato (se a regra for Inclusive) ou não procurar (se a regra for Exclusive).
A regra do exemplo da figura 2, define que todos os snapshots do Hudson serão sempre encontrados no repositório Snapshots. Isso fará com que o Nexus nem tente procurar por estes artefatos nos demais repositórios.
Qual será o impacto disso na build? Repetindo o mesmo teste, e tomando o cuidado de apagar os artefatos do diretório .m2 do computador local, para forçar que todos os artefatos sejam recuperados do Nexus, o tempo de build caiu para incríveis 2 minutos e 17 segundos!
Obviamente, este teste representa uma situação extrema, com muitos artefatos e diversos respositórios externos. Provavelmente projetos pequenos e com poucos artefatos não terão um impacto tão significativo no tempo de build, mas à medida que os projetos crescem e os builds ficam mais complexas, um bom ajuste nas configurações do Nexus pode ser a solução para diminuir o seu tempo de build.
A técnica de configurar roteamento no Nexus pode ser útil em outras situações também. Por exemplo, se existe um mesmo artefato em dois repositórios diferentes e se quer garantir que o artefato seja recuperado sempre de um determinado repositório, é possível criar uma regra para isso. Enfim, existem inúmeras possibilidades. Basta encontrar aquela que melhor se encaixa no seu cenário.
Em resumo, para diminuir o tempo de build de um projeto usando o Nexus, é importante:
- Configurar a ordem dos repositórios nos grupos de forma que o repositório que contém o maior número de artefatos utilizados apareça no topo da lista;
- Criar regras de roteamento para garantir que os artefatos internos só sejam procurados nos repositórios internos.
Os resultados dos testes descritos foram conseguidos um ambiente de desenvolvimento na nuvem hosteado na comunidade ToolsCloud. Se você tem interesse em acompanhar notícias do Nexus e outras ferramentas, basta seguir @toolscloud no Twitter.