Encerramento gracioso de um aplicativo de inicialização Spring

Encerramento gracioso de um aplicativo de inicialização Spring

1. Visão geral

No desligamento, por padrão,TaskExecutor do Spring simplesmente interrompe todas as tarefas em execução, mas pode ser bom esperar que todas as tarefas em execução sejam concluídas. Estegives a chance for each task to take measures to ensure the shutdown is safe.

Neste tutorial rápido, aprenderemos como fazer esse desligamento mais suave de um aplicativo Spring Boot quando envolver tarefas em execução usando pools de threads.

2. Exemplo Simples

Vamos considerar um simples aplicativo Spring Boot. Faremos automaticamente o beanTaskExecutor padrão:

@Autowired
private TaskExecutor taskExecutor;

Na inicialização do aplicativo, vamos executar um processo de 1 minuto de duração usando um thread do pool de threads:

taskExecutor.execute(() -> {
    Thread.sleep(60_000);
});

Quando umshutdown é iniciado, por exemplo, 20 segundos após a inicialização, o encadeamento no exemplo é interrompido e o aplicativo é encerrado imediatamente.

3. Aguarde a conclusão das tarefas

Vamos mudar o comportamento padrão do executor de tarefa criando um beanThreadPoolTaskExecutor personalizado.

Esta classe fornece um sinalizadorsetWaitForTasksToCompleteOnShutdown para evitar a interrupção de tarefas em execução. Vamos defini-lo paratrue:

@Bean
public TaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(2);
    taskExecutor.setMaxPoolSize(2);
    taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    taskExecutor.initialize();
    return taskExecutor;
}

E vamos reescrever a lógica anterior para criar 3 threads, cada um executando uma tarefa de 1 minuto de duração.

@PostConstruct
public void runTaskOnStartup() {
    for (int i = 0; i < 3; i++) {
        taskExecutor.execute(() -> {
            Thread.sleep(60_000);
        });
    }
}

Vamos agora iniciar um desligamento nos primeiros 60 segundos após a inicialização.

Vemos que o aplicativo é encerrado apenas 120 segundos após a inicialização. O tamanho do conjunto 2 permite que apenas duas tarefas simultâneas sejam executadas, de modo que a terceira seja colocada na fila.

Definir o sinalizador garante queboth the currently executing tasks and queued up tasks are completed.

Observe que quando uma solicitação de desligamento é recebida, otask executor closes the queueso that new tasks can’t be added.

4. Tempo máximo de espera antes da rescisão

Embora tenhamos configurado para esperar a conclusão de tarefas em andamento e enfileiradas, Springcontinues with the shutdown of the rest of the container. Isso pode liberar os recursos necessários ao executor de tarefas e causar falhas nas tarefas.

A fim de bloquear o desligamento do restante do contêiner, podemos especificar um tempo de espera máximo noThreadPoolTaskExecutor:

taskExecutor.setAwaitTerminationSeconds(30);

Isso garante que, para o período de tempo especificado, oshutdown process at the container level will be blocked.

Quando definimos o sinalizadorsetWaitForTasksToCompleteOnShutdown paratrue, precisamos especificar um tempo limite significativamente maior para que todas as tarefas restantes na fila também sejam executadas.

5. Conclusão

Neste tutorial rápido, vimos como encerrar com segurança um aplicativo Spring Boot configurando o bean do executor de tarefas para concluir as tarefas em execução e enviadas até o final. Isso garante que todas as tarefas terão o tempo indicado para concluir seu trabalho.

Um efeito colateral óbvio é que também podelead to a longer shutdown phase. Portanto, precisamos decidir se deve ou não usá-lo, dependendo da natureza do aplicativo.

Como sempre, os exemplos deste artigo estão disponíveisover on GitHub.