Frühjahr DeferredResult Ursachen IOException: Eine bestehende Verbindung wurde abgebrochen, indem die software in Ihre host-Maschine

Ich versuche, Spring DeferredResult durchzuführen long polling. In diesem Beispiel, wenn ein Benutzer besucht eine Seite mit long polling zu warten, für einen anderen Benutzer auf einen link zu klicken. Ein zweiter Benutzer (den Sie in einem anderen browser) klickt dann auf diesen link, und die long polling zurück, um den ersten Benutzer, wobei Ihr der zweite Benutzer auf.

Die jsp sieht wie folgt aus:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Spring Example</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
    <script>
    function pollContent() {
        $.ajax({url: "waitForClick", success: function(result){
            console.log("Polled result: " + result);
            $("#polledContent").html(result);
            pollContent();
        }});
    }
    $(pollContent);
    </script>
  </head>
<body>
    <p><a href="clickTheThing">Click this thing.</a></p>
    <p id="polledContent">Waiting for somebody to click the thing...</p>
</body>
</html>

Und der controller sieht wie folgt aus:

package com.example.controller;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult;

import com.example.controller.interfaces.ExampleControllerInterface;

@Component
public class ExampleController implements ExampleControllerInterface{

    private int clickCount = 0;

    private List<DeferredResult<String>> waiting = new ArrayList<>();

    @Override
    public String viewHomePage(HttpServletRequest request, ModelMap model, HttpSession session){
        return "index";
    }

    @RequestMapping(value = "/clickTheThing", method = RequestMethod.GET)
    public String clickTheThing(HttpServletRequest request, ModelMap model, HttpSession session){

        new Thread(){
            public void run(){

                clickCount++;
                System.out.println("Somebody clicked the thing! Click count: " + clickCount);
                Iterator<DeferredResult<String>> iterator = waiting.iterator();
                while(iterator.hasNext()){
                    DeferredResult<String> result = iterator.next();
                    System.out.println("Setting result.");
                    result.setResult("Somebody clicked the thing! Click count: " + clickCount);
                    iterator.remove();
                }
            }
        }.start();

        return "clicked";
    }

    @ResponseBody
    @RequestMapping(value = "/waitForClick", method = RequestMethod.GET)
    public DeferredResult<String> waitForClick(HttpServletRequest request, ModelMap model, HttpSession session){
        final DeferredResult<String> result = new DeferredResult<>();
        waiting.add(result);
        return result;
    }

    @ResponseBody
    @RequestMapping(value = "/getClickCount", method = RequestMethod.GET)
    public String getClickCount(HttpServletRequest request, ModelMap model, HttpSession session){
        return String.valueOf(clickCount);
    }
}

Und der Vollständigkeit halber, hier ist mein ErrorConfig Klasse:

package com.example.config;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class ErrorConfig{

    @ExceptionHandler(Exception.class)
    public String handleException (HttpServletRequest request, HttpServletResponse response, HttpSession session, Exception e) {
        e.printStackTrace();
        return "index";
    }
}

Diese scheint zu funktionieren okay. Der erste Benutzer ist in der Tat informiert, sobald ein anderer Benutzer auf den link klickt.

Jedoch, wenn die ersten user die Seite aktualisiert, bevor der zweite Benutzer auf den link klickt, bekomme ich auch einen stack-trace für alle "alten" DeferredResult:

org.apache.catalina.connector.ClientAbortException: java.io.IOException: An established connection was aborted by the software in your host machine
    at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:396)
    at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:426)
    at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:342)
    at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:316)
    at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:110)
    at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:297)
    at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141)
    at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229)
    at org.springframework.util.StreamUtils.copy(StreamUtils.java:106)
    at org.springframework.http.converter.StringHttpMessageConverter.writeInternal(StringHttpMessageConverter.java:106)
    at org.springframework.http.converter.StringHttpMessageConverter.writeInternal(StringHttpMessageConverter.java:40)
    at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:208)
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:143)
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:89)
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:193)
    at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:71)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:122)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:301)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:721)
    at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:639)
    at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:605)
    at org.apache.catalina.core.AsyncContextImpl$1.run(AsyncContextImpl.java:208)
    at org.apache.catalina.core.AsyncContextImpl.doInternalDispatch(AsyncContextImpl.java:363)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:214)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:136)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:78)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:405)
    at org.apache.coyote.http11.AbstractHttp11Processor.asyncDispatch(AbstractHttp11Processor.java:1636)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:646)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1566)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1523)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: An established connection was aborted by the software in your host machine
    at sun.nio.ch.SocketDispatcher.write0(Native Method)
    at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:51)
    at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
    at sun.nio.ch.IOUtil.write(IOUtil.java:65)
    at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:470)
    at org.apache.tomcat.util.net.NioChannel.write(NioChannel.java:122)
    at org.apache.tomcat.util.net.NioBlockingSelector.write(NioBlockingSelector.java:101)
    at org.apache.tomcat.util.net.NioSelectorPool.write(NioSelectorPool.java:173)
    at org.apache.coyote.http11.InternalNioOutputBuffer.writeToSocket(InternalNioOutputBuffer.java:139)
    at org.apache.coyote.http11.InternalNioOutputBuffer.addToBB(InternalNioOutputBuffer.java:197)
    at org.apache.coyote.http11.InternalNioOutputBuffer.access$000(InternalNioOutputBuffer.java:41)
    at org.apache.coyote.http11.InternalNioOutputBuffer$SocketOutputBuffer.doWrite(InternalNioOutputBuffer.java:320)
    at org.apache.coyote.http11.filters.IdentityOutputFilter.doWrite(IdentityOutputFilter.java:84)
    at org.apache.coyote.http11.AbstractOutputBuffer.doWrite(AbstractOutputBuffer.java:257)
    at org.apache.coyote.Response.doWrite(Response.java:523)
    at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:391)
    ... 50 more
Aug 20, 2015 7:19:24 PM org.apache.catalina.core.ApplicationDispatcher invoke
SEVERE: Servlet.service() for servlet jsp threw exception
java.lang.IllegalStateException: getOutputStream() has already been called for this response
    at org.apache.catalina.connector.Response.getWriter(Response.java:535)
    at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:212)
    at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:103)
    at org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:115)
    at org.apache.jasper.runtime.JspWriterImpl.flushBuffer(JspWriterImpl.java:108)
    at org.apache.jasper.runtime.PageContextImpl.release(PageContextImpl.java:173)
    at org.apache.jasper.runtime.JspFactoryImpl.internalReleasePageContext(JspFactoryImpl.java:120)
    at org.apache.jasper.runtime.JspFactoryImpl.releasePageContext(JspFactoryImpl.java:75)
    at org.apache.jsp.WEB_002dINF.jsp.index_jsp._jspService(index_jsp.java:93)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:403)
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:347)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:301)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:721)
    at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:584)
    at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:523)
    at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:201)
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:267)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1221)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1005)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:952)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:301)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:721)
    at org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:639)
    at org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:605)
    at org.apache.catalina.core.AsyncContextImpl$1.run(AsyncContextImpl.java:208)
    at org.apache.catalina.core.AsyncContextImpl.doInternalDispatch(AsyncContextImpl.java:363)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:214)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:136)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:78)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:405)
    at org.apache.coyote.http11.AbstractHttp11Processor.asyncDispatch(AbstractHttp11Processor.java:1636)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:646)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1566)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1523)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

Kann ich das einfach ignorieren, diese Ausnahmen, aber das fühlt sich falsch an.

So, meine Fragen sind:

  • Was die Ursache der ursprünglichen ClientAbortException? Sollte ich etwas anderes zu tun?
  • Was sind die besten Methoden für diese Art der Sache ist, vorzugsweise in einer Weise, die nicht generieren Ausnahmen?
  • Gibt es einen besseren Weg zu verfolgen, DeferredResults für long polling?
  • Gibt es eine Möglichkeit, um die nachfolgenden getOutputStream() has already been called for this response Ausnahmen, die ich sammeln sind, verursacht durch die Fehler-Seite von der exception-handler?

Ich habe eine mavenized version des Projekts verfügbar auf GitHub hier wenn Sie wollen, um es selbst zu versuchen.

Am Ende bin ich versucht, hinzufügen von Benachrichtigungen system, um meine Spring-website, ähnlich wie StackOverflow-Benachrichtigungs-system. Wenn es einen besseren Weg zu tun, mit Feder und long-polling, bin ich alle Ohren.

Edit: ich habe nicht alle empfangenen Antworten (oder auch Kommentare), so habe ich ein Kopfgeld. Ich würde auf jeden Fall schätzen jedes feedback!

  • Als eine alternative auf, wie zu tun, dass im Frühling: ich würde WebSockets.
  • Dank Andrei. Ich sehe, dass StackOverflow nutzt WebSockets, also ich nehme an, Sie sind eine Untersuchung Wert. Ich war wirklich gehofft, um es arbeiten mit DeferredResult. Gut, einen Plan B wenn.
  • Die ClientAbortException wurde verursacht durch die IOException, wie die stack-trace sagt ganz klar, und es gibt zahlreiche bestehende Fragen hier über, die.
  • Ich habe gesehen, dass diejenigen Fragen, aber keiner von Ihnen scheinen zu meinem Szenario. Ich würde gerne herausfinden, zu jeder spezifischen Frage, die Sie denken, erklärt mein problem.
  • Ich konnte deinen Fehler nicht reproduzieren. Bitte erklären Sie im detail die Aktionen von "Benutzer1" und " Benutzer2? Ich sehe nur Timeout Fehler, wenn User1 wartet auf von User2 aber User2 nichts zu tun.
  • Stellen Sie sicher, dass die ErrorConfig richtig eingerichtet. Ohne Sie, wird der Fehler nur schweigend gegessen. Hier sind die Schritte, die ich ergreifen würde, um zu wiederholen, die Fehlermeldung: Benutzer-1 besucht den index. (Erstellt eine DeferredResult, die gespeichert wird.) Benutzer 1 aktualisiert. (Dies erzeugt ein weiteres DeferredResult, die gespeichert wird.) Benutzer 1 aktualisiert wieder. (Ein Weiteres DeferredResult.) User 2 Besuche den index. Benutzer 2 klickt auf den "Klicken Sie auf diese Sache" - link. Die letzten DeferredResult gibt richtig, aber die ersten 2 Fehler auslösen.
  • Wich Tomcat-Version, die Sie arbeiten?
  • Ich arbeite mit Tomcat 8.
  • Ich habe versucht das Problem zu reproduzieren, mit reinigt instalations tomcat 8.0.1 und tomcat 8.0.26... Welche genaue version sind yo versucht gegen? Auch haben Sie keine änderungen an der Konfiguration von tomcat? Wenn ja, welche änderungen? können Sie versuchen, mit einer crean 8.0.26 installieren?
  • Ich werde überprüfen, wenn ich nach Hause komme später heute, aber stellen Sie sicher, dass Sie die ErrorConfig richtig eingestellt. Ohne es, ist dieser Fehler einfach ignoriert.
  • Ich kann bestätigen, das ErrorConfig geladen wird (ich habe ein trace im Konstruktor) und, dass nichts angezeigt wird, wenn: die Eröffnung von zwei Browsern auf '/', erfrischend der erste browser, klicken auf 'es' in der zweiten.
  • "Eine bestehende Verbindung wurde abgebrochen, indem die software in Ihre host-Maschine". Ich bin strating zu denken, es könnte sein OS bezogenen. Ich bin auf windoof, wo sind Sie?
  • Gleiche. Windows 7. Aber es passiert auch auf einem linux-server.
  • Das ist, was ich verwende... Dann hab ich keine Ahnung, warum es funktioniert gut in meiner Suche... kann sein das es browser verwandt ist. Trotzdem überlegt der Kunde plötzlich geschlossen, die Verbindung mit dem server... das ist ein Fehler, wie bewiesen durch die SO link in meiner Antwort, und es gibt keine Weise, die ich mir denken kann, es zu vermeiden (anders als einige schmutzige synchrone Ajax-request in das onbeforeunload-Ereignis). Wenn es standardmäßig ausgeblendet, es sei denn, Sie fügen die ErrorConfig, dann denke ich, sollten Sie es ignorieren... wie auch immer, ich empfehlen Ihnen dringend, zu versuchen, die Atmosphäre dieses zu lösen.
  • Dein code ist nicht thread-safe. Sie benötigen einen AtomicInteger anstelle von int -, concurrent-queue anstelle einer array-Liste, eine periodische Aufgabe zur Verarbeitung der Warteschlange anstelle von Laich-threads selbst. Sie sollten check out dieses Beispiel MVC-Projekt von Spring mit Beispielen DeferredResult.
  • Hat die thread-Sicherheit zu tun haben mit der ich das problem beschrieben?
  • Nicht wirklich, nur einige Verbesserungen in Bezug auf.

Schreibe einen Kommentar