当前位置: 首页 > 知识库问答 >
问题:

泽西岛:异步请求后立即响应

朱自明
2023-03-14

我试图理解异步响应在Jersey上的工作方式。我阅读了新泽西文档(https://jersey.java.net/documentation/latest/async.html)的第10章,但它对我的问题没有帮助。这里关于stackoverflow的研究也没有得出令人满意的答案(这一点我可以理解)。

我试图做的与本文中的一个问题类似(使用http状态202进行异步操作)。我想使用HTML表单文档将一个大文件上传到服务器。在请求被发送到服务器之后,web服务应该立即响应状态202和URI,在请求完成后可以在该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秒等待后收到“唤醒!”消息,而不是202响应,这似乎是合乎逻辑的,因为AsyncACK是处理响应的消息(据我所知)。

在阅读文档后,我得到的印象是,这是应该发生的,因为泽西岛对异步服务器响应所做的一切就是将线程从响应线程池外包到另一个线程池,以释放处理时间,以便对服务进行更多响应。

所以我的两个问题是:我的理解是否正确,我可以使用异步服务器响应来获得所需的行为吗?

我尝试在不使用<code>异步响应

任何回应都非常感谢。

问候语

共有2个答案

刘弘济
2023-03-14

我最近经历了同样的痛苦。泽西岛一直声称它支持异步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 {

因此,我没有与它作斗争,而是使用上面的代码来至少使其正确。理想情况下,我希望泽西岛不要使用“位置”标题。

总之,上面的代码就是我使用的代码(不包括业务逻辑位;)

黎腾
2023-03-14

你的问题混合了两个主题。

从HTTP的角度来看,202在技术上是一个完整的请求。而请求的结果是202,服务器告诉你它会在边上做。您必须发出另一个HTTP请求来获取更新的状态。

从应用程序的角度来看,异步意味着您将以单独的线程(或其他异步方式)执行请求。但是,这也意味着,在另一个“veryExpensiveOperation”完成之前,您不会返回结果,甚至不会返回202。跳过这个环的全部目的是释放调用线程。您的web服务器数量有限,例如20个,如果您的每个请求都花费了很长时间,那么所有20个请求都将挂起。使用@Suspended,您可以将执行从html" target="_blank">web服务器线程转移到其他方式(在您的情况下是另一个线程)。这实际上只是第一步。异步服务器背后的思想是,即使是非常昂贵的操作也是以某种异步方式实现的,因此等待数据库或文件不会占用整个线程。

 类似资料:
  • 我想返回一个临时重定向,使用AsyncACK。 下面的“工作”(因为没有错误),但似乎不是异步的(它一次处理一个请求)。 这应该工作吗?如果我明确需要像https://jersey.github.io/documentation/latest/async.html#d0e9895一样启动一个新线程,返回响应是什么样子的?

  • 问题内容: 我有一个Jersey REST API,并且正在使用来处理授权。我还在所有端点上使用,以便我的API可以处理数千个并发请求。 我的授权过滤器命中了一个远程服务,但是当运行过滤器时,Jersey尚未将当前线程添加到它的内部,因此我完全失去了异步的好处。 我可以告诉Jersey我希望这是异步的吗? 这是一个示例资源: 更新 刚刚收到泽西队球员的回音,从2.7开始,这是不可能的。只有资源方法

  • 我正在尝试找到一种异步的方式来立即返回客户端请求的响应。 我所需要的只是记录请求数据,调用新线程请求其他服务器上昂贵的操作(一些后端操作),并且无需等待它们的响应,立即返回200状态响应给客户端。 此时此刻,我正试图用完整的未来来做这件事,但我错过了一些东西。 总之,我得到了立即响应,但veryExpensiveOperations()方法似乎丢失了httpRequest值,这对我来说太糟糕了,因

  • 我目前使用的是球衣 我现在要做的是设置泽西,这样当查询参数进来时(比如缩进),我可以告诉Jackson以“更漂亮的格式,也就是缩进”序列化JSON。您可以通过使用SerializationConfig.Feature.INDENT_OUTPUT配置JSON映射器来轻松地告诉Jackson这样做。 问题是,我如何在每个请求的基础上获取一个queryparam并使用它来修改Jackson的输出?

  • 问题内容: 我在Glassfish上有一个工作正常的JavaEE 6 Web应用程序,该应用程序已经具有JSF前端并具有其身份验证机制((使用基于CDI和注释的安全性),因此有一个登录屏幕,用户输入用户名密码,按登录按钮和Java EE身份验证过程开始。 现在,我想“还将”我的一些服务类公开为REST服务(我可能会使用Jersey),因此也可以从移动设备访问它。但是让我担心的是登录部分。 我将使用

  • 更新刚刚听到从泽西的家伙,这是不可能的2.7。只有资源方法本身被异步调用,而不是筛选器。仍然欢迎任何关于继续进行的建议。