2017-06-02 1 views
1

J'ai une ressource Class, avec une classe de méthode @ManagedAsync qui ressemble à ceci:JAX-RS dans Dropwizard: Gestion des appels async avec réponse immédiate

@Path("my-resource") 
public class MyResource extends BaseResource{ 

    private DatumDAO datumDAO; 

    public MyResource(DatumDAO datumDAO){ 
     this.datumDAO = datumDAO; 
    } 

    public void cleanDatum(Datum datum){ 
     //time taking operations 
    } 

    @GET 
    @ManagedAsync 
    @Path("/cleanup/{from}/{till}/") 
    @Consumes(MediaType.APPLICATION_JSON) 
    @Produces(MediaType.APPLICATION_JSON) 
    @UnitOfWork 
    public void cleanupDirtyData(@Suspended final AsyncResponse asyncResponse, @PathParam("from") DateTimeParam from, 
      @PathParam("till") DateTimeParam till) throws IOException{ 

     logger.debug("will try to cleanup dirty data in range: " + from + " " + till); 
     List<Datum> data = datumDAO.getALlDirtyDatumInRange(from.get().toDate(), till.get().toDate()); 
     Map<Long,String> cleanupMap = new HashMap<Long,String>(); 
     for(Datum datum: data){ 
      cleanDatum(datum); 
      cleanupMap.put(datum.getId(), "cleaned"); 
     } 
     // this response need to be sent [can be ignored]  
     asyncResponse.resume(Response.status(HttpStatus.OK_200).entity(cleanupMap).build()); 

    } 

} 

Depuis l'appel à cleanupDirtyData peut prendre un certain temps, je ne Je ne veux pas que les clients l'attendent complètement, je comprends que le travail d'exécution est déchargé sur un thread de travail différent.

Ce que je suis en train de réaliser est de donner une réponse immédiate au client et continuer à faire l'exécution de la fonction cleanupDirtyData de manière asynchrone.

donc essayé la chose suivante:

Mettre un délai d'attente de force, et de donner une réponse prématurée au client, mais cela ne semble pas être le moyen idéal et il arrête l'exécution.

qui ressemblerait à quelque chose comme ceci:

@Path("my-resource") 
public class MyResource extends BaseResource{ 

    private DatumDAO datumDAO; 

    public MyResource(DatumDAO datumDAO){ 
     this.datumDAO = datumDAO; 
    } 

    public void cleanDatum(Datum datum){ 
     //time taking operations 
    } 

    @GET 
    @ManagedAsync 
    @Path("/cleanup/{from}/{till}/") 
    @Consumes(MediaType.APPLICATION_JSON) 
    @Produces(MediaType.APPLICATION_JSON) 
    @UnitOfWork 
    public void cleanupDirtyData(@Suspended final AsyncResponse asyncResponse, @PathParam("from") DateTimeParam from, 
      @PathParam("till") DateTimeParam till) throws IOException{ 

     // Register handler and set timeout 
     asyncResponse.setTimeoutHandler(new TimeoutHandler() { 
      public void handleTimeout(AsyncResponse ar) { 
       asyncResponse.resume(Response.status(SERVICE_UNAVAILABLE).entity(
        "Operation timed out -- please try again").build());      
       } 
     }); 
     ar.setTimeout(15, TimeUnit.SECONDS);  

     logger.debug("will try to cleanup dirty data in range: " + from + " " + till); 
     List<Datum> data = datumDAO.getALlDirtyDatumInRange(from.get().toDate(), till.get().toDate()); 
     Map<Long,String> cleanupMap = new HashMap<Long,String>(); 
     for(Datum datum: data){ 
      cleanDatum(datum); 
      cleanupMap.put(datum.getId(), "cleaned"); 
     } 
     // this response need to be sent [can be ignored]    
     asyncResponse.resume(Response.status(HttpStatus.OK_200).entity(cleanupMap).build()); 

    } 

} 
+1

_ "Étant donné que l'appel de cleanupDirtyData peut prendre un certain temps, je ne veux pas que les clients l'attendent complètement" _ - Vous voulez savoir pourquoi cela n'a pas de sens? Parce que dans votre code, vous renvoyez un corps de réponse au client. Si ce corps de réponse fait partie de "l'exécution longue", comment voulez-vous renvoyer la réponse au client lorsque l'exécution pour obtenir le corps n'est pas terminée? Si je me trompe, quelle est exactement la tâche à long terme dans votre code? –

+0

@peeskillet c'était un code d'espace réservé, je n'ai pas besoin de retourner une réponse une fois le travail terminé. Les tâches de longue durée impliquent beaucoup d'E/S de disques, ce qui peut prendre jusqu'à 15 minutes. – anand

Répondre

5

Le JAX-RS Asynchronous API Server est tout sur la façon dont le conteneur gèrera la demande. Mais il tiendra toujours la demande et n'affectera pas l'expérience client.

Citant la documentation Jersey sur le Asynchronous Server API:

Notez que l'utilisation du modèle de traitement asynchrone côté serveur ne améliore pas le temps de traitement des demandes perçue par le client. Il augmentera cependant le débit du serveur en libérant le thread de traitement de la demande initiale dans le conteneur d'E/S tandis que la demande peut toujours attendre dans une file d'attente pour traitement ou le traitement peut encore être exécuté sur un autre serveur dédié fil. Le thread de conteneur d'E/S libéré peut être utilisé pour accepter et traiter les nouvelles connexions de demande entrantes .

Si vous voulez donner au client une réponse immédiate, vous cherchez peut-être quelque chose comme:

@Singleton 
@Path("expensive-task") 
public class ExpensiveTaskResource { 

    private ExecutorService executor; 

    private Future<String> futureResult; 

    @PostConstruct 
    public void onCreate() { 
     this.executor = Executors.newSingleThreadExecutor(); 
    } 

    @POST 
    public Response startTask() { 
     futureResult = executor.submit(new ExpensiveTask()); 
     return Response.status(Status.ACCEPTED).build(); 
    } 

    @GET 
    public Response getResult() throws ExecutionException, InterruptedException { 
     if (futureResult != null && futureResult.isDone()) { 
      return Response.status(Status.OK).entity(futureResult.get()).build(); 
     } else { 
      return Response.status(Status.FORBIDDEN).entity("Try later").build(); 
     } 
    } 

    @PreDestroy 
    public void onDestroy() { 
     this.executor.shutdownNow(); 
    } 
} 
public class ExpensiveTask implements Callable<String> { 

    @Override 
    public String call() throws Exception { 

     try { 
      Thread.sleep(10000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     return "Task completed"; 
    } 
} 

Dans un conteneur de servlet, vous pouvez utiliser un ExecutorService pour exécuter votre tâche coûteuse . Dans un conteneur Java EE, vous devez envisager un ManagedExecutorService.

+0

belle explication +1. –