web-dev-qa-db-ger.com

Wie funktionieren Server-Sent-Events?

Ich habe ein SSE (Server-Sent-Events) mit Java unter Tomcat 8.0 ausprobiert. Hier sind einige Dinge, die mir aufgefallen sind.

Ich klicke auf eine Schaltfläche, die automatisch eine Anfrage an das Servlet sendet. Die GET-Methode von Servlet wird ausgeführt und gibt einen Ereignisstrom zurück. Sobald der vollständige Stream empfangen wurde, stellt die Seite automatisch eine weitere Anfrage, die dieselben Daten erneut empfängt. Ich habe dort keine Endlosschleife !!!

  1. Was passiert eigentlich auf dem Server? In normalen Szenarien erstellt Tomcat einen Thread, um jede Anfrage zu bearbeiten. Was passiert gerade?

  2. Wie stellen Sie sicher, dass der Ereignisstrom nur einmal an dieselbe Verbindungs-/Browsersitzung gesendet wird? 

  3. Wie stellen Sie sicher, dass der Ereignisstrom geschlossen ist und auf dem Server kein Ressourcen-Overhead entsteht?

  4. Unterscheidung zwischen GET- und POST -Anfragen. Warum hat es sich für GET entschieden?

  5. Ist es zu früh, um SSE auf Tomcat zu verwenden? Probleme mit der Leistung?

Hier ist der Code für Neugierige,

@WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        //content type must be set to text/event-stream
        response.setContentType("text/event-stream"); 
        //cache must be set to no-cache
        response.setHeader("Cache-Control", "no-cache");     
        //encoding is set to UTF-8
        response.setCharacterEncoding("UTF-8");

        PrintWriter writer = response.getWriter();

        for(int i=0; i<10; i++) {
            System.out.println(i);
            writer.write("data: "+ i +"\n\n");
            writer.flush();
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        writer.close(); 
    }
}

Javascript auf der Seite (ich habe nichts anderes auf der Seite),

<button onclick="start()">Start</button>

<script type="text/javascript">
    function start() {
        var eventSource = new EventSource("TestServlet");
        eventSource.onmessage = function(event) {
            console.log("data: "+event.data)
            document.getElementById('foo').innerHTML = event.data;
        };
    }
</script>

Versuchte dies mit CURL. Und die Antwort kam nur einmal. Ich benutze Chrom, also muss dies ein Problem mit Chorme sein?

EDIT:

Was ich gelernt und gelernt habe, ist jetzt in meinem Blog dokumentiert - Server Sent Events

12
John

Ändern Sie diese Zeile 

writer.write("data: "+ i +"\n\n");

zu

writer.write("data: "+ i +"\r\n");

Übrigens hat Ihr Code ein schwerwiegendes Leistungsproblem, da er einen Thread enthält, bis alle Ereignisse gesendet werden. Verwenden Sie stattdessen die asynchrone Verarbeitungs-API. z.B.

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    AsyncContext actx = req.startAsync();
    actx.setTimeout(30*1000);
    //save actx and use it when we need sent data to the client.
}

Dann können wir AsyncContext später verwenden

//write some data to client when a certain event happens
actx.getResponse().getWriter().write("data: " + mydata + "\r\n");
actx.getResponse().getWriter().flush();

wenn alle Ereignisse gesendet wurden, können wir sie schließen

actx.complete();

UPDATE 1:

Wir müssen die Ereignisquelle im Browser schließen, wenn der Server nicht erneut mit dem Server verbunden werden soll, wenn der Server die Antwort abschließt. 

eventSource.close();

Eine andere Methode hilft vielleicht, nämlich. Wir haben eine ziemlich lange Wiederholungszeit eingestellt, aber ich habe es nicht versucht, z.

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    AsyncContext actx = req.startAsync();
    actx.getResponse().getWriter().write("retry: 36000000000\r\n"); // 10000 hours!
    actx.getResponse().getWriter().flush();
    //save actx and use it when we need sent data to the client.
}

UPDATE 2:

Ich denke, Websocket ist vielleicht besser für Ihren Fall.

UPDATE 3: (beantworte die Fragen)

  1. Was passiert eigentlich auf dem Server? In normalen Szenarien erstellt Tomcat einen Thread, um jede Anfrage zu bearbeiten. Was passiert gerade?

Wenn Sie einen NIO-Connector verwenden, der in Tomcat 8.0.X standardmäßig verwendet wird, enthält die HTTP-E/A zu einer Anforderung im gesamten Verarbeitungszyklus keinen Thread. Bei Verwendung von BIO wird ein Thread angehalten, bis der gesamte Verarbeitungszyklus abgeschlossen ist. Alle Threads stammen aus einem Thread-Pool. Tomcat erstellt keinen Thread für jede Anforderung.

  1. Wie stellen Sie sicher, dass der Ereignisstrom nur einmal an dieselbe Verbindungs-/Browsersitzung gesendet wird?

Do eventSource.close() auf der Browserseite ist die beste Wahl.

  1. Wie stellen Sie sicher, dass der Ereignisstrom geschlossen ist und auf dem Server kein Ressourcen-Overhead entsteht?

Vergessen Sie nicht, AsyncContext.complete () auf der Serverseite aufzurufen.

  1. Unterscheidung zwischen GET- und POST -Anfragen. Warum hat es sich für GET entschieden?

Die EventSource-API in einem Browser unterstützt nur GET-Anforderungen. Auf der Serverseite gibt es keine solche Einschränkung. SSE wird hauptsächlich verwendet, um Ereignisdaten vom Server zu empfangen. Wenn ein Ereignis eintritt, kann der Browser das Ereignis rechtzeitig erhalten, und es ist nicht erforderlich, eine neue Anfrage zu erstellen, um es abzufragen. Wenn Sie eine Vollduplex-Kommunikation benötigen, probieren Sie WebSocket instread von SSE.

  1. Ist es zu früh, um SSE auf Tomcat zu verwenden? Probleme mit der Leistung?

Es sollten keine Leistungsprobleme auftreten, wenn wir einen NIO-Connector und eine asynchrone Verarbeitungs-API verwenden. Ich weiß nicht, ob der Tomcat NIO-Connector ausgereift ist oder nicht, aber etwas wird nie bekannt sein, wenn wir es nicht versuchen.

21
xfeep

Ich empfehle dringend, zuerst Stream Updates mit Server-Sent Events zu lesen, um ein allgemeines Verständnis für die Technologie zu erhalten .. _. Dann folgen Sie Server-Sent Events mit Async Servlet anhand von Beispiel , um zu sehen, wie SSE kann speziell mit der Servlet-Technologie verwendet werden. 

0
01es