web-dev-qa-db-fra.com

Comment Server-Sent-Events fonctionne

J'ai essayé un SSE (Server-Sent-Events) à l'aide de Java sur Tomcat 8.0. Voici quelques choses que j'ai remarquées.

Je clique sur un bouton qui envoie automatiquement une demande au servlet. La méthode GET de Servlet est exécutée et renvoie un flux d'événements. Une fois que le flux complet est reçu, la page à nouveau fait automatiquement une autre demande qui reçoit à nouveau les mêmes données !!! Je n'ai pas de boucle infinie là-bas !!!

  1. Qu'est-ce qui se passe réellement sur le serveur? Dans des scénarios normaux, Tomcat crée un thread pour gérer chaque demande. Qu'est ce qu'il se passe maintenant?

  2. Quelle est la bonne façon de s’assurer que le flux d’événements n’est envoyé qu’une seule fois à la même session de connexion/navigateur? 

  3. Quelle est la bonne façon de vous assurer que le flux d'événements est fermé et qu'aucune surcharge de ressources ne se produit sur le serveur?

  4. Comment différencier les requêtes GET des POST. Pourquoi a-t-il choisi GET?

  5. Est-il trop tôt pour utiliser SSE sur Tomcat? Des problèmes de performance?

Voici le code pour les curieux,

@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 sur la page (je n'ai rien d'autre sur la page),

<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>

Essayé cela en utilisant CURL. Et la réponse est venue juste une fois. J'utilise du chrome, donc cela doit être un problème avec chorme ??

MODIFIER:

Ce que j'ai appris et appris est maintenant documenté dans mon blog - Server Sent Events

12
John

Changer cette ligne 

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

à

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

En passant, votre code aura un sérieux problème de performances car il tiendra un thread jusqu'à l'envoi de tous les événements. Utilisez plutôt l'API de traitement asynchrone. par exemple.

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.
}

Ensuite, nous pouvons utiliser AsyncContext plus tard

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

si tous les événements envoyés nous pouvons le fermer

actx.complete();

MISE À JOUR 1:

Si vous ne souhaitez pas que le navigateur reconnecte le serveur une fois que le serveur a terminé la réponse, vous devez fermer la source d'événements sur le navigateur. 

eventSource.close();

Une autre méthode peut être utile, à savoir. nous avons défini un temps de tentative assez long mais je ne l'ai pas essayé, par exemple.

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.
}

MISE À JOUR 2:

Je pense que Websocket est peut-être préférable pour votre cas.

MISE À JOUR 3: (répondez aux questions)

  1. Qu'est-ce qui se passe réellement sur le serveur? Dans des scénarios normaux, Tomcat crée un thread pour gérer chaque demande. Qu'est ce qu'il se passe maintenant?

Si vous utilisez le connecteur NIO qui est la valeur par défaut dans Tomcat 8.0.X, tout au long du cycle de traitement, les E/S HTTP relatives à une demande ne contiendront pas de thread. Si vous utilisez BIO, un thread sera maintenu jusqu'à la fin du cycle de traitement. Tous les threads proviennent d'un pool de threads, Tomcat ne créera pas de thread pour chaque demande.

  1. Quelle est la bonne façon de s’assurer que le flux d’événements n’est envoyé qu’une seule fois à la même session de connexion/navigateur?

Faire eventSource.close() du côté du navigateur est le meilleur choix.

  1. Quelle est la bonne façon de vous assurer que le flux d'événements est fermé et qu'aucune surcharge de ressources ne se produit sur le serveur?

N'oubliez pas d'appeler AsyncContext.complete () côté serveur.

  1. Comment différencier les requêtes GET des POST. Pourquoi a-t-il choisi GET?

L’API EventSource dans un navigateur ne prend en charge que les demandes GET, mais il n’existe pas de telle restriction du côté serveur . SSE est principalement utilisé pour recevoir des données d’événements du serveur. Si un événement se produit, le navigateur peut le recevoir à temps et il n'est pas nécessaire de créer une nouvelle demande pour l'interroger . Si vous avez besoin d'une communication en duplex intégral, essayez WebSocket au lieu de SSE.

  1. Est-il trop tôt pour utiliser SSE sur Tomcat? Des problèmes de performance?

Il ne devrait y avoir aucun problème de performances si nous utilisons un connecteur NIO et une API de traitement asynchrone. Je ne sais pas si le connecteur Tomcat NIO est mature ou non, mais quelque chose ne sera jamais connu à moins de l'essayer.

21
xfeep

Je recommande fortement de commencer par lire Diffuser les mises à jour avec les événements envoyés par le serveur pour bien comprendre la technologie . Suivez ensuite les événements envoyés par le serveur avec un servlet async par exemple pour voir comment SSE peut être utilisé spécifiquement avec la technologie Servlet. 

0
01es