2017-06-02 83 views
1

我有一個資源Class,具有@ManagedAsync方法類看起來像這樣:JAX-RS在Dropwizard:處理異步調用與即時響應

@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()); 

    } 

} 

由於呼叫cleanupDirtyData可能需要一段時間,我不我不希望客戶完全等待它,我知道執行工作被卸載到不同的工作線程。

我想要實現的是立即響應客戶端,並不斷異步執行函數cleanupDirtyData

所以嘗試了以下事情:

把一個有力超時,並給予客戶一個不成熟的迴應,但似乎並沒有得到理想的方式,它停止執行。

會是這個樣子:

@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

_「由於調用cleanupDirtyData可能需要一段時間,我不希望客戶端完全等待它。」 - - 你想知道爲什麼這樣做沒有意義嗎?因爲在你的代碼中你正在向客戶端返回一個響應體。如果響應主體是「長時間運行的執行」的一部分,那麼在執行獲取主體的操作未完成時,您希望如何將響應返回給客戶端?如果我錯了,代碼中長期運行的任務究竟是什麼? –

+0

@peeskillet它是一個佔位符代碼,在作業完成後我不需要返回任何響應。長時間運行的任務涉及大量磁盤I/O,可能需要15分鐘。 – anand

回答

5

JAX-RS異步服務器API是所有關於容器將如何管理的要求。但它仍然會保留請求,不會影響客戶體驗。

引述有關Asynchronous Server API澤西文檔:

注意的是,使用服務器端的異步處理模式將 不會改善客戶感知的請求處理時間。然而, 將通過將 初始請求處理線程釋放回I/O容器而增加服務器的吞吐量,而 請求可能仍在隊列中等待處理或者 處理可能仍在另一專用上運行線。 發佈的I/O容器線程可用於接受和處理新的 傳入請求連接。

如果你想給客戶立即作出反應,你可能會尋找類似:

@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"; 
    } 
} 

在一個servlet容器,你可以使用ExecutorService來運行你昂貴的任務。在Java EE容器中,您應該考慮使用ManagedExecutorService

+0

很好的解釋+1。 –