使用deferredResult

时间:2015-07-20 11:33:25

标签: java spring-mvc

为了实施长轮询,我尝试了不同的解决方案并没有获得任何好结果。

所以我决定研究异步方法和DeferredResult。 在这里,我实现了REST constroller。

@Controller("sessionStateRest")
@RequestMapping("ui")
public class SessionStateRest extends BaseRestResource {

    private final Queue<DeferredResult<ModelAndView>> mavQueue = new ConcurrentLinkedQueue<>();

    /**
     * Rest to check session state.
     *
     * @return string with session state
     */
    @RequestMapping(value = "/session")
    public @ResponseBody DeferredResult<ModelAndView> sessionState() {
        final DeferredResult<ModelAndView> stateResult = new DeferredResult<>();
        this.mavQueue.add(stateResult);
        return stateResult;
    }

    @Scheduled(fixedDelay = 5000)
    public void processQueue() {
        for(DeferredResult<ModelAndView> result: mavQueue) {
            if (null == SecurityHelper.getUserLogin()) {
                result.setResult(createSuccessResponse("Invalidated session"));
                mavQueue.remove(result);
            }
        }
    }
}

根据想法,它应该每隔5秒处理一次请求队列,如果条件为真则处理setResult

同步版本就是这样的

@RequestMapping(value = "/sync")
public ModelAndView checkState() {
    if (null == SecurityHelper.getUserLogin()) {
        createSuccessResponse("Invalidated session");
    }
    return null; // return something instead
}

但过了一段时间我有一个例外

java.lang.IllegalStateException: Cannot forward after response has been committed
        at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:349) ~[tomcat-embed-core-7.0.
39.jar:7.0.39]
        at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:339) ~[tomcat-embed-core-7.0.39
.jar:7.0.39]
        at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:467) [tomcat-embed-core-7.0.39.jar:7.0.3
9]
        at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:338) [tomcat-embed-core-7.0.39.jar:7.0.3
9]
        at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:428) [tomcat-embed-core-7.0.39.jar:7.
0.39]
        at org.apache.catalina.core.AsyncContextImpl.setErrorState(AsyncContextImpl.java:417) [tomcat-embed-core-7.0.39.jar:
7.0.39]
        at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:294) [tomcat-embed-core-7.0.39.jar:7
.0.39]
        at org.apache.coyote.http11.AbstractHttp11Processor.asyncDispatch(AbstractHttp11Processor.java:1567) [tomcat-embed-c
ore-7.0.39.jar:7.0.39]
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:583) [tomcat-embed-cor
e-7.0.39.jar:7.0.39]
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312) [tomcat-embed-core-7.0.39.jar:7.
0.39]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [na:1.7.0_67]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [na:1.7.0_67]
        at java.lang.Thread.run(Thread.java:745) [na:1.7.0_67]

有什么问题?我应该设置DeferredResult的超时时间吗?

1 个答案:

答案 0 :(得分:2)

我认为问题来自@ResponseBody注释。它告诉Spring,控制器方法将直接返回响应的主体。但它没有,因为它返回ModelAndView。因此Spring尝试将方法的返回直接发送到客户端,(并且应该发送并提交空响应),然后ModelAndView处理程序尝试转发到已经提交的响应导致错误的视图。

您至少应该删除@ResponseBody注释,因为它不是同步等价物。

但那不是全部:

  • 你写final DeferredResult<... - 恕我直言,决赛不应该出现,因为你稍后会修改DeferredResult
  • 您尝试在计划的异步线程中测试已记录的用户。这应该不起作用,因为常见的SecurityHelper使用本地线程存储来存储此信息,并且实际处理将在另一个线程中发生。 DeferredResult的Javadoc甚至说:例如,人们可能希望通过扩展类并为用户添加附加属性来关联用于创建DeferredResult的用户。通过这种方式,用户可以在以后轻松访问,而无需使用数据结构进行映射。
  • 您没有说明如何配置异步支持。 Spring参考手册说: MVC Java配置和MVC命名空间都提供了配置异步请求处理的选项。 WebMvcConfigurer方法configureAsyncSupport,而<mvc:annotation-driven><async-support>子元素。