【问题标题】:Send Spring response stream immediately rather than wait until method return立即发送 Spring 响应流,而不是等到方法返回
【发布时间】:2016-09-29 19:42:39
【问题描述】:

我有一个如下所示的请求映射:

private final static byte[] byteArray = ...;

@RequestMapping(value=Array("/foobar"))
void sendByteArray(@RequestBody Request request, OutputStream os) {
  os.write(byteArray);
  os.flush();
  doLengthyCleanup();
}

我发现请求客户端直到服务完成 doLengthyCleanup() 之后才真正收到响应正文。

由于清理不会影响响应本身,我想通过在发送响应后执行清理来缩短响应时间。我该怎么做?

【问题讨论】:

  • 一种解决方案是异步执行清理。
  • 我期望 Spring 实际上会通过输出流同步传递字节,从而使任何类型的线程都变得不必要,并使事情变得更简单。似乎在 Spring 中应该有一个一流的支持方法来执行此操作,它比创建一个全新的线程或绑定到 HandlerInterceptorAdapter 更简单。
  • 字节数组是类成员吗?有状态的控制器不是线程安全的。
  • @doge byteArray 只是为了说明。实际上,控制器根据请求本身生成响应值,但我认为这与手头的问题无关。为简单起见,您可以将byteArray 视为静态内容。
  • 打电话给response.flush()怎么样? Spring 会以某种方式知道没有更多内容要写入流,或者您应该明确地 flush 它。

标签: java spring


【解决方案1】:
@RequestMapping(value=Array("/foobar"))
void sendByteArray(@RequestBody Request request, OutputStream os) {
  os.write(byteArray);
  os.flush(); // not sure
  doLengthyCleanup(); 
}

@Async
void doLengthyCleanup() {
  // this will be executed asynchronously
}

更新:取自this question

如果您从同一类中的另一个方法调用 @Async 方法,除非您为 @EnableAsync 启用 AspectJ 代理模式(当然并提供一个编织器),否则将无法正常工作(谷歌“代理自调用” )。最简单的解决方法是将@Async 方法放在另一个@Bean 中。

【讨论】:

  • 我比我的替代解决方案更喜欢这个,因为我不必自己管理响应对象。
  • 还有为什么不将字节数组作为ResponseBody返回呢?
  • 我只是尝试这样做。如果我之后返回byte[],对cleanUp() 的调用似乎不会异步执行。唔。也许检查这方面的文档,我可能需要添加一些配置。
【解决方案2】:

this answer所示,您需要通过直接接受响应并自己设置状态码来向Spring表明您正在自己处理响应:

void sendByteArray(@RequestBody Request request, HttpServletResponse response) {
  response.setStatus(HttpStatus.SC_OK);
  OutputStream os = response.getOutputStream();
  os.write(byteArray);
  os.flush();
  os.close();
  doLengthyCleanup();
}

【讨论】:

  • 嘿,我尝试了您的解决方案,但没有奏效。在 wr.close() 之后,我将 sleep 方法放置了 10 秒。当我点击请求时,它会等待 10 秒发送回响应。 PFB 代码。输出流.close(); System.out.println("流关闭。"); System.out.println("睡觉");尝试{ Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("唤醒");如果我遗漏了什么,你能帮忙吗?
  • @gargvive 上面的代码在 3 年前对我来说工作得很好,但是在我的答案的 cmets 中调试你的代码是不可能的。我建议创建一个minimal, reproducible example 并提出您自己的问题。
  • 肯定会这样做。只是一个简单的问题,应用程序是否需要在 spring-boot 中才能使上述代码工作。我们有一个在 tomcat7 上运行的应用程序,并将 spring-core 作为依赖项之一。我之所以问,是因为我在 Spring Boot 应用程序中尝试过同样的事情并且它有效。但是我们正在尝试实施的项目在 tomcat 上运行。
猜你喜欢
  • 1970-01-01
  • 2023-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-25
  • 2013-05-12
相关资源
最近更新 更多