Como aquecer as classes java para evitar a primeira chamada lenta?

13

Estou fazendo um projeto em que preciso que todas as chamadas de API levem menos de 1s, mas estou enfrentando um problema com a primeira chamada em cada rota mais lenta que as seguintes.

Atualmente, a primeira chamada para / login leva 3,6s e as próximas levam 170ms e o mesmo para todas as outras rotas.

Descobri usando -XX:+TraceClassLoadingisso na primeira chamada, as classes foram carregadas na memória e isso causou um problema de desempenho.

No entanto, não encontrei uma maneira fácil de carregar todas as classes na inicialização e, para cada novo serviço, preciso adicionar uma chamada de aquecimento em um ApplicationRunner.

Alguém tem uma solução para carregar automaticamente as classes de um aplicativo SpringBoot ou aquecer todas as suas rotas?

Ybri
fonte
Você pode adicionar mais detalhes? Seu aplicativo está instanciando controladores? Ou você está ligando para outros serviços? Como você está fazendo ligações para outros serviços?
Menios
O Spring Boot usa a varredura de classe intensivamente, para que você não precise 'aquecer' algo como no aplicativo de desktop. Esse carregamento inicial longo pode ser resultado da pesquisa de recursos - por exemplo, carregamento de modelo de página.
Alex Chernyshev
Um pouco de uma abordagem indireta: se você tiver 100% de cobertura de teste de unidade para os pontos de extremidade, poderá usá-los. Você ainda teria de código por endpoint mas você ganhar alguma coisa
Marged
11
Pode não ser o ideal, dependendo do projeto que você está realizando, mas você pode chamar seus endpoints internamente quando o aplicativo for carregado.
omoshiroiii 04/02
@omoshiroiii não há nada de errado nisso. nós fazemos. em produção. o motivo tem a ver com algumas bibliotecas dinâmicas que usam invokedynamice sabemos que a resolução é lenta na primeira chamada para elas (temos dezenas de milhares de chamadas, que sem essa primeira chamada acumulam dezenas de segundos).
Eugene

Respostas:

1

O carregamento de classe do Java é lento. Isso significa que uma classe é carregada apenas pela JVM quando e quando necessário.

Se você deseja forçá-lo a carregar ansiosamente classes, basta referenciá-las. Uma maneira de fazer isso é percorrer o conteúdo do jar ou os arquivos de classe para obter os nomes das classes e usá-los para chamar Class.forName(className).

Além disso, se o tempo e o desempenho da inicialização forem muito importantes para o seu caso de uso, convém procurar soluções de compilação antecipadamente , como o GraalVM , ou reduzir o limite do JIT para compilação ( -XX:CompileThreshold).

andresp
fonte
nenhum deles resolverá o problema do OP. o carregamento ainda é preguiçoso no GraalVM e, na verdade, JITé sem sentido nas primeiras invocações.
Eugene
também GraalVMé bom, mas observe o número de problemas no github: assim que você passar de um projeto de sandbox para algo maior (eu estou olhando sua reflexão, principalmente), você sentirá alguma dor, finalmente. meu ponto é: trocar para o GraalVM não é um simples estalar de dedos.
Eugene
Pensei em carregar as classes na jarra, mas não encontrei uma maneira de fazê-lo, você teria um exemplo?
Ybri
@ Eugene, se você ler minha resposta, verá que eu não disse o GraalVM ou o limite do JIT mudaria a preguiça do carregamento de classes. A resposta à pergunta do OP sobre preguiça é o parágrafo anterior a esse. O último parágrafo é apenas uma dica adicional, caso o OP precise otimizar ainda mais o tempo / desempenho de inicialização além do carregamento da classe.
andresp
11
@Ybri, há outras perguntas com respostas para isso aqui, por exemplo, stackoverflow.com/questions/2370867/…
andresp
0

Para mim, a única opção viável que você tem é class data sharingespalhada pelo JEP 310 , JEP 341 e JEP 350 , mas isso requer o java-13 provavelmente. Estamos testando isso internamente no meu local de trabalho (principalmente por diversão, não vou mentir) e os resultados parecem bons até agora.

A outra opção está chamando seus terminais quando o aplicativo é iniciado - se essa é uma opção. Novamente, é para nós, por exemplo: nós os chamamos com dados fictícios algumas centenas de vezes para aquecer o código. Mas, ao mesmo tempo, temos serviços onde isso seria impossível - é por isso que a exploração CDStambém.

Eugene
fonte
Como você lida com a autenticação e os terminais de postagem para evitar a criação de dados no banco de dados de produção?
Ybri
@ Ybri exatamente por que eu disse, impossível para alguns.
Eugene