Java Jersey:异步请求后的即时响应
我试图理解异步响应与Jersey的工作方式。我阅读了Jersey文档()的第10章,但它对我的问题没有帮助。这里关于stackoverflow的研究也没有得出令人满意的答案(我可以理解) 我想做的与本文中的一个问题类似()。我想使用HTML表单文档将一个大文件上载到服务器。将请求发送到服务器后,web服务应立即响应状态202和URI,请求完成后可在其中找到文件 在阅读了这篇文章之后,似乎有可能,但遗憾的是,没有提示如何在给定的情况下实现这样的行为 我编写了一个小型web服务来测试功能:Java Jersey:异步请求后的即时响应,java,multithreading,rest,asynchronous,jersey,Java,Multithreading,Rest,Asynchronous,Jersey,我试图理解异步响应与Jersey的工作方式。我阅读了Jersey文档()的第10章,但它对我的问题没有帮助。这里关于stackoverflow的研究也没有得出令人满意的答案(我可以理解) 我想做的与本文中的一个问题类似()。我想使用HTML表单文档将一个大文件上载到服务器。将请求发送到服务器后,web服务应立即响应状态202和URI,请求完成后可在其中找到文件 在阅读了这篇文章之后,似乎有可能,但遗憾的是,没有提示如何在给定的情况下实现这样的行为 我编写了一个小型web服务来测试功能:
@Path("/test/async/")
public class TestAsyncResponse {
@GET
@Path("get")
public Response asyncGet(@Suspended final AsyncResponse response) {
new Thread(new Runnable() {
@Override
public void run() {
DateFormat df = new SimpleDateFormat("dd/MM/yy HH:mm:ss");
System.out.println("#### thread started: "
+ df.format(new Date()) + " ####");
String result = veryExpensiveOperation();
System.out.println("#### thread finished: "
+ df.format(new Date()) + " ####");
response.resume(result);
}
private String veryExpensiveOperation() {
try {
Thread.sleep(10000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
return "Woke up!";
}
}).start();
return Response.status(202).entity("Request accepted. " +
"Long running operation started")
.build();
}
}
该服务可以工作,但作为一个响应,我在等待10秒后收到了“wakeup!”消息,而不是202响应,这似乎是合乎逻辑的,因为AsyncResponse
是处理响应的响应(据我所知)
在阅读了文档之后,我得到了这样的印象:这是应该发生的,因为Jersey对异步服务器响应所做的一切就是将响应线程池中的线程外包给另一个线程池,以便为服务的更多响应腾出处理时间
所以我的两个问题是:我的理解正确吗?我可以使用异步服务器响应来获得所需的行为吗
我试图在没有AsyncResponse
的情况下启动一个新线程,我得到了一个NullPointerException
,因为Jersey已经关闭了响应,因此关闭了包含文件数据的InputStream
。这是预期的行为吗?这篇文章()似乎表明它可能有效
非常感谢您的回复
问候您的问题混合了两个主题 从HTTP的角度来看,202在技术上是一个已完成的请求。请求的结果是202,服务器告诉您它将在一旁执行。您必须发出另一个HTTP请求才能获得更新状态
从应用程序的角度来看,异步意味着您将以单独的线程(或其他异步方式)执行请求。但是,这也意味着在另一个“非常昂贵的操作”完成之前,您不会返回结果,甚至不会返回202。跳过这个环的全部要点是释放呼叫线程。您的web服务器数量有限,例如20个,如果您的每个请求都花费了很长时间,那么所有20个请求都将挂起。使用@Suspended可以将执行从web服务器线程转移到其他方式(在您的情况下是另一个线程)。这实际上只是第一步。异步服务器背后的想法是,即使是非常昂贵的操作也是以某种异步方式实现的,因此等待数据库或文件不会占用整个线程。我最近也经历了同样的痛苦。Jersey一直声称它支持异步REST调用,但我认为这是不诚实的。 事实上,一旦我开始想出正确的方法,泽西实际上就挡住了我的路
private static ExecutorService executorService = Executors.newFixedThreadPool( Integer.valueOf( numberOfThreads ) );
@POST
@Path("async")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response async( @FormDataParam("file") InputStream inputStream,
@FormDataParam("file") FormDataContentDisposition des ) throws Throwable {
String uniqueID = UUID.randomUUID().toString();
executorService.execute( new Runnable() {
@Override
public void run() {
try {
// do long performing action
} catch (Exception ex) {
}
}
} );
return Response.accepted().location( getResultsURI( uniqueID ) ).build();
}
@GET
@Path("results/{uniqueID}")
@Produces("application/zip")
public Response results( @PathParam(value = "uniqueID ") String uniqueID ) {
// Check status of job
// If not finished...
if (notFinished) {
return Response.status( 202 ).location( getResultsURI( uniqueID ) )
.entity( status ).build();
}
return Response.ok( FileUtils.readFileToByteArray( zip.toFile() ) ).type( "application/zip" )
.header( "Content-Disposition", "attachment; filename=\"filename.zip\"" ).build();
}
protected URI getResultsURI( String uniqueID ) throws URISyntaxException {
return new URI( Constants.WS_VERSION + "/results/" + uniqueID );
}
最大的痛苦是,当您设置Response.location()时,即使您将其设置为“/results”或“/results”,Jersey也会将其扩展到完整的URL。这很好,只是它忽略了任何类级别的@Path:
@Path(Constants.WS_VERSION)
public class MyEndpoint {
因此,我没有与之抗争,而是使用上面的代码来至少纠正它。理想的情况下,我希望泽西队不要在“位置”标题上动
不管怎样-上面的代码就是我使用的(不包括业务逻辑位;)你明白了吗?Jersey是否提供了支持返回的自动方法?虽然我很欣赏@Suspended是一种异步、代理/防火墙等,但他们并不这么认为——他们将其视为一个长期运行的HTTP请求。202只是另一个返回代码,您可以通过正常方式返回它。我试图解释的是,这些是完全不同的主题@挂起允许您释放容器线程。202允许您构建自己的更高级别协议。这两者通常不一起使用。