Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

330
Views
Dónde crear ExecutorServices y cuándo cerrarlos

Estoy creando un servicio REST usando Spring con Jersey y tengo un caso de uso en el que por cada solicitud que recibo, necesito hacer varias llamadas (N) a una API ascendente.

Recibo una solicitud, tiene n elementos, para cada elemento, creo un hilo para llamar a mi dependencia (REST) y procesar la respuesta. Al final recopilo todas las respuestas, manteniendo el orden, y las devuelvo como una sola respuesta al cliente.

Estoy usando CompletableFuture de Java 8 y quería saber si estaba usando el marco ExecutorService correctamente.

 @Component // automatic singleton by Spring class A { private ExecutorService executorService = Executors.newCachedThreadPool(); private RawResponse getRawResponse(Item item) { // make REST call } private Response processResponse(RawResponse rawResponse) { // process response } public List<Response> handleRequest(Request request) { List<CompletableFuture> futureResponses = new ArrayList<>(); for(Item item : request.getItems()) { CompletableFuture<Response> futureResponse = CompletableFuture.supplyAsync(() -> getRawResponse(item), executorService) .thenApply(rawResponse -> processResponse(rawResponse)) .handle((response, throwable) { if(throwable != null) { // log and return default response } else { return response;}}); futureResponses.add(futureResponse); } List<Response> result = new ArrayList<>(); for (CompletableFuture<Resource> futureResponse : futureResponses) { try { result.add(futureResponse.get()); } catch (Exception e) { // log error } } return result; } }

La pregunta que tengo ahora es si debo mover la creación del executorService justo arriba:

 List<CompletableFuture> futureResponses = new ArrayList<>();

y llame a shutdown justo arriba:

 return result;

porque en este momento, realmente no estoy llamando al apagado en ningún lado ya que la aplicación siempre se ejecutará en su contenedor docker.

¿Es costoso seguir creando y descartando el grupo, o la forma actual perderá memoria? Y creo que llamar al grupo estático como una variable de campo privado es redundante ya que la clase es un frijol de primavera de todos modos (singleton).

Cualquier consejo será apreciado, ¿también debería usar un grupo de subprocesos en caché? No estoy seguro de cómo aproximar el número de hilos que necesito.

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

¿Debo mover la creación del executorService justo arriba?

No, no lo hace, tiene su ExecutorService en el lugar correcto en su código de ejemplo. Piense en ello como un grupo de subprocesos, no querrá iniciar un nuevo grupo de subprocesos y cerrarlo para cada llamada de método de handleRequest . Por supuesto, ExecutorService hace más trabajo que un grupo de subprocesos, en realidad administrará un grupo de subprocesos debajo y proporciona administración del ciclo de vida para tareas asíncronas.

Realmente no estoy llamando al apagado en ninguna parte ya que la aplicación siempre se ejecutará en su contenedor acoplable.

En la mayoría de los casos, inicia su ExecutorService cuando se inician las aplicaciones y lo cierra cuando se cierran las aplicaciones. Así que puede dejarlo allí, porque se cerrará cuando la aplicación salga, o puede agregar algún tipo de enlace de shutdown hooks si necesita hacer un apagado correcto.

¿Es costoso seguir creando y descartando el grupo?

Más o menos, no queremos crear y descartar Thread muy a menudo, por lo que tenemos un grupo de subprocesos, si crea/descarta un grupo para cada llamada de método, ¿cuál es el punto de tener un grupo de subprocesos?

¿O la forma actual va a perder memoria?

No, siempre y cuando la tarea que envió no pierda memoria. La implementación de ExecutorService en sí es buena para usar.

Y creo que llamar al grupo estático como una variable de campo privado es redundante ya que la clase es un frijol de primavera de todos modos (singleton)

Sí, tienes razón. También puede definir ExecutorService como Spring Bean e inyectarlo en el bean de servicio, si desea realizar algún proceso de inicio personalizado.

si debo usar un grupo de subprocesos en caché, no estoy seguro de cómo aproximar la cantidad de subprocesos que necesito.

Eso es difícil de decir, necesita hacer algunas pruebas para obtener la cantidad correcta de subprocesos para su aplicación. Pero la mayor parte del marco NIO o EventDriven tiene el doble de núcleos disponibles que el número de subprocesos de forma predeterminada.

over 4 years ago · Santiago Trujillo Report

0

Como está utilizando Spring, es posible que desee dejar que se encargue de la ejecución asíncrona.

Simplemente coloque @EnableAsync en una de sus clases de @Configuration para habilitar la anotación @Async en los métodos.

Luego cambiaría su getRawResponse() a

 @Async private CompletableFuture<RawResponse> getRawResponse(Item item) { // make REST call return CompletableFuture.completedFuture(rawResponse); }

(Es posible que deba colocar este método en un servicio separado para permitir un proxy adecuado, según cómo esté configurado AOP en su proyecto)

y cambia tu ciclo a simplemente

 for(Item item : request.getItems()) { CompletableFuture<Response> futureResponse = getRawResponse(item) .thenApply(rawResponse -> processResponse(rawResponse)) .handle((response, throwable) { if(throwable != null) { // log and return default response } else { return response;} }); futureResponses.add(futureResponse); }

Como puede ver, ya no necesita preocuparse por el albacea en su servicio.

También puedes personalizar tu ejecutor declarando su bean Spring, por ejemplo:

 @SpringBootApplication @EnableAsync public class Application extends AsyncConfigurerSupport { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(2); executor.setMaxPoolSize(2); executor.setQueueCapacity(500); executor.setThreadNamePrefix("GithubLookup-"); executor.initialize(); return executor; } }

Incluso puede configurar varios ejecutores y seleccionar uno proporcionando su nombre como parámetro para la anotación @Async .

Consulte también Primeros pasos: creación de métodos asíncronos y la anotación @Async .

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!