如何在超时时使用Spring SseEmitter避免HttpMediaTypeNotAcceptableException

问题描述 投票:1回答:2

当SseEmitter超时时,我收到错误HttpMediaTypeNotAcceptableException

在SO中搜索此错误代码时,我只找到与为ResponseBody注册转换器相关的答案。但我无法看到这可能是我遇到的同样问题,因为内容类型是text/event-stream并由Spring自动处理。

然而,沟通似乎流动良好;邮件正在正确发送给客户端。

@RequestMapping(path = "/channel")
@CrossOrigin
public SseEmitter channel() throws IOException {

    SseEmitter emitter = new SseEmitter();

    // Storing the emitter
    // ...
    player.setEmitter(emitter);

    return emitter;
}

在播放器中,我这样做:

public void setEmitter(SseEmitter emitter) {
    if (this.emitter != null) {
        // Old emitter must be completed for user
        this.emitter.complete();
    }

    this.emitter = emitter;
    this.emitter.onTimeout(() -> {
        if (this.emitter != null) this.emitter.complete();
    });
    this.emitter.onCompletion(() -> this.emitter = null);
}

public void sendMessage(String message) {
    if (this.emitter != null) {
        emitter.send(message);
    }
}

(顺便说一下,这是处理发射器的好方法吗?即onTimeout和onCompletion的注册?想法是每次重新连接时都会创建一个新的发射器,这意味着需要更换旧的发射器。)

发送的消息是序列化为JSON文本的数据结构。在JavaScript客户端上,它被正确地反序列化为JSON对象。我没有在任何地方指定JSON内容,因为我认为从SseEmitter / SSE的角度来看,它只是文本。

在连接建立期间,一切正常。在每次超时时,客户端都会按原样重新连接。但是,我似乎总是在超时和重新连接过程中遇到以下异常:

org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:191) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:183) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:81) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:687) ~[javax.servlet-api-3.1.0.jar:3.1.0]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) ~[javax.servlet-api-3.1.0.jar:3.1.0]
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:812) ~[jetty-servlet-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:587) ~[jetty-servlet-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) ~[jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:595) ~[jetty-security-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223) ~[jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ContextHandler.__doHandle(ContextHandler.java:1127) ~[jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java) ~[jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515) ~[jetty-servlet-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185) ~[jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061) ~[jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) ~[jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97) ~[jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.Server.handleAsync(Server.java:549) ~[jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:348) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.HttpChannel.run(HttpChannel.java:262) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635) [jetty-util-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555) [jetty-util-9.2.14.v20151106.jar:9.2.14.v20151106]
    at java.lang.Thread.run(Thread.java:745) [na:na]

我正在使用Spring Boot和Jetty。我也得到了这个错误,但我认为这是第一个错误:

java.lang.IllegalStateException: Committed
    at org.eclipse.jetty.server.Response.resetBuffer(Response.java:1243) ~[jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.Response.sendError(Response.java:567) ~[jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.Response.sendError(Response.java:544) ~[jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:167) ~[javax.servlet-api-3.1.0.jar:3.1.0]
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:167) ~[javax.servlet-api-3.1.0.jar:3.1.0]
    at org.springframework.security.web.context.OnCommittedResponseWrapper.sendError(OnCommittedResponseWrapper.java:95) ~[spring-security-web-4.0.3.RELEASE.jar:4.0.3.RELEASE]
    at org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.handleHttpMediaTypeNotAcceptable(DefaultHandlerExceptionResolver.java:257) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.doResolveException(DefaultHandlerExceptionResolver.java:121) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:137) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.handler.HandlerExceptionResolverComposite.resolveException(HandlerExceptionResolverComposite.java:74) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.processHandlerException(DispatcherServlet.java:1185) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1022) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:973) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:687) [javax.servlet-api-3.1.0.jar:3.1.0]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [javax.servlet-api-3.1.0.jar:3.1.0]
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:812) [jetty-servlet-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:587) [jetty-servlet-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:595) [jetty-security-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ContextHandler.__doHandle(ContextHandler.java:1127) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515) [jetty-servlet-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.Server.handleAsync(Server.java:549) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:348) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.server.HttpChannel.run(HttpChannel.java:262) [jetty-server-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635) [jetty-util-9.2.14.v20151106.jar:9.2.14.v20151106]
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555) [jetty-util-9.2.14.v20151106.jar:9.2.14.v20151106]
    at java.lang.Thread.run(Thread.java:745) [na:na]
spring spring-mvc server-sent-events
2个回答
0
投票

错误不在服务器端。在客户端上点击几次后,突然出现了两个客户端EventSource对象。这就是导致错误的原因。


0
投票

在基于servlet的服务器(如jetty或tomcat)上发生此错误,因为servlet api在客户端连接关闭时不会通知。找出连接是否已关闭的唯一方法是发送消息,并在触发IOException时停止sseemitter。

问题是IOException也被调度到spring mvc,无法找到合适的httpmessageconverter,因此抛出了HttpMediaTypeNotAcceptableException

最好的选择是将sse发射器迁移到netty驱动的微服务。在我的情况下,我不得不坚持tomcat。以下是我在春季启动应用程序中解决它的方法:

  • 我添加了一个自定义httpmessagecomverter来处理媒体类型文本/事件流的Map
  • 我使用spring web @ExceptionHandler添加了一个错误处理程序来静音ClientAbortException
© www.soinside.com 2019 - 2024. All rights reserved.