Partage
  • Partager sur Facebook
  • Partager sur Twitter

SSE vs Long-Polling

Sujet résolu
    20 décembre 2024 à 16:46:20

    Bonjour, je viens de lire un article : PHP: Long-Polling vs WebSockets vs SSE vs Comet et je voudrais vos avis pour mon cas.

    J'ai mis en place depuis cette option "SSE" sur mon site pour émettre continuellement les messages. Mais j'ai un problème avec : EventSource bloque les nouvelles requetes,je ne sais pas si cette erreur est liée au front ou au back mais ça me fait réfléchir sur la meilleure option pour ce que je veux.

    Sur l'application mobile je fais des requêtes style "Long-Polling". Je fais une requête pour charger les messages de l'utilisateur chaque 03 secondes, lorsque la page de la discussion est ouverte, et je mets à jour s'il y'a de nouveaux messages. S'il change de page(écran/vue) j'arrête. Les alertes en temps réelles sur les nouveaux messages lorsqu'il est sur d'autres pages de l'application pourront se faire avec les notifications (Firebase). Pensez vous que je dois utiliser les SSE pour la communication sur mobile avec le serveur pour ce cas ?

    function send_all_messages()
    {
        set_time_limit(0);
        ini_set('display_errors', 'off');
        // ignore_user_abort(true);
    
        header('Cache-Control: no-cache');
        header('Content-Type: text/event-stream');
        header('Connection: keep-alive');
    
        require_once('includes/repositories/repository-discussion.php');
        require_once('includes/repositories/repository-message.php');
        $discussionRepo = new DiscussionRepository();
        $messageRepo = new MessageRepository();
        $user = wp_get_current_user();
        $userId = get_current_user_id();
    
        $maxExecutionTime = 110;
        $startTime = time();
        $checkInterval = 3;
    
        while (true) {
            if (connection_aborted() || (time() - $startTime) >= $maxExecutionTime) {
                break;
            }
    
            try {
                $discussions = $discussionRepo->getAllForUser($user);
    
                if ($discussions !== null) {
                    $hasUpdates = false;
                    $messagesToArray = [];
    
                    foreach ($discussions as $discussion) {
                        $unreadCount = $messageRepo->getUnreadMessagesCount($discussion->id, $userId);
    
                        if ($unreadCount > 0) {
                            $messages = $messageRepo->getAllForDiscussion($discussion->id);
                            if (!empty($messages)) {
                                $hasUpdates = true;
                                foreach ($messages as $message) {
                                    $messagesToArray[] = $message->toArray(true);
                                }
                            }
                        }
                    }
    
                    if ($hasUpdates) {
                        $response = json_encode([
                            'messages_pack' => $messagesToArray,
                            'user_id' => $userId,
                            'timestamp' => time()
                        ]);
    
                        echo "event: updated\n";
                        echo "data: $response\n\n";
    
                        if (ob_get_level() > 0) {
                            ob_end_flush();
                        }
                        flush();
                    }
                }
            } catch (Exception $e) {
                error_log("SSE Error: " . $e->getMessage());
                echo "event: error\n";
                echo "data: {\"error\": \"Server error occurred\"}\n\n";
                flush();
            }
    
            sleep($checkInterval);
        }
    
        echo "event: close\n";
        echo "data: {\"message\": \"Stream closed\"}\n\n";
        flush();
        exit(0);
    }

    C'est un site Wordpress

    add_action('parse_request', 'check_paths');
    function check_paths()
    {
        $current_url = $_SERVER['REQUEST_URI'];
        ...
        if($_SERVER['REQUEST_METHOD'] === 'GET'){
            ...
            if (str_contains($current_url,  '/charger-messages')) {
                send_all_messages();
                exit;
            }
    ...
    }




    -
    Edité par Asmitta 22 décembre 2024 à 7:45:18

    • Partager sur Facebook
    • Partager sur Twitter
      21 décembre 2024 à 23:59:41

      Salut

      Est-ce qu'une partie du message ne serait pas passée ?

      Néanmoins, je me risquerais à relever une chose : il semble que tu envoies l'événement close dans tous les cas, est-ce que c'est bien ce que tu souhaites ? Sachant que pour du SSE, il n'y a pas vraiment besoin d'un événement explicite en ce sens, sauf si l'application doit réagir spécifiquement à un tel événement, ce qui, de ce que je comprends, n'arrive que dans certains cas et pas tout le temps.

      • Partager sur Facebook
      • Partager sur Twitter
        22 décembre 2024 à 7:44:52

        > il semble que tu envoies l'événement close dans tous les cas, est-ce que c'est bien ce que tu souhaites ?

        C'est hors de la boucle, je pense que c'est exécuté seulement si l'utilisateur "se déconnectez" ou si le temps d'exécution est long

        >sauf si l'application doit réagir spécifiquement à un tel événement,

        Non, je ne fais rien avec cet évènement en faite. Je me disais que c'était un norme. Ah je viens de checker, ça ne sert à rien (pour moi)...

        >Est-ce qu'une partie du message ne serait pas passée ?

        Oui, il manquait ce lien: EventSource bloque les nouvelles requetés par Asmitta - page 1 - OpenClassrooms, j'ai mis à jour le premier message.

        Bonjour

        • Partager sur Facebook
        • Partager sur Twitter
          23 décembre 2024 à 0:21:14

          Asmitta a écrit:

          C'est hors de la boucle, je pense que c'est exécuté seulement si l'utilisateur "se déconnectez" ou si le temps d'exécution est long

          De ce que j'ai compris des SSE, la partie émettrice n'envoie que les données nécessaires, et termine sa transmission avec deux retours à la ligne consécutifs. Ces deux retours à la ligne signifient une fin de transmission, après quoi le script émetteur peut se terminer. Le script qui reçoit sait qu'il ne recevra pas d'autre donnée, et reprendra contact automatiquement après le temps imparti. Je pense que la manière que j'exprime ici par "reprendre contact" n'est pas précisément important au niveau purement technique. Ce que j'y entends, c'est que l'URL qui est fourni à l'objet EventSource peut être prévue pour ne renvoyer qu'un unique événement puis s'arrêter, pas besoin d'une boucle comme on pourrait le voir dans certains exemples. Ceux-ci peuvent induire aussi en erreur dans la mesure où une suite d'événement est parfois montrée comme un seul bloc de texte, comme s'il s'agissait d'un seul envoi, or, à moins que lesdits événement soient différents (un ajout, une suppression et une mise à jour entre deux demandes par exemple), cela peut très bien être en plusieurs envois distincts.

          Dans un de mes projets Symfony avec un système de gestion de commandes, j'ai ce qui suit.

          const eventSource = new EventSource('{{ url('production_events') }}');
          eventSource.onmessage = function(e) {
          	const orders = JSON.parse(e.data);
          	for (const item of orders) {
          		// Ici je fais ce que j'ai à faire avec les commandes
          		// reçues du serveur
          	}
          };
          
              #[Route('/production/events', name: 'production_events', methods: ['GET'])]
              public function productionEvents(Request $request, OrderLineRepository $orderLineRepository): Response
              {
                  $lastUpdate = new \DateTimeImmutable($request->headers->get('last-event-id', 'now'));
                  $retry = $this->getParameter('production_sse_frequency');
                  $response = new Response(headers: ['Content-Type' => 'text/event-stream']);
                  $quantities = $orderLineRepository->getToProduceQuantity($lastUpdate->modify('+1 second'));
                  return $this->render('production.txt.twig', [
                      'quantities' => $quantities,
                      'retry' => $retry
                  ], $response);
              }
          id: {{ quantities[0].lastOrder|default('now')|date_modify('+1 second')|date('Y-m-d\\TH:i:s.u') }}
          data: {{ quantities|json_encode }}
          retry: {{ retry }}000
           
           
          

          J'ai bien mes commandes mises à jour pour la partie front, et ma partie back ne fait qu'envoyer les informations nécessaires à un moment. J'ai quelques informations supplémentaires pour savoir quand a eu lieu ma dernière mise à jour en front, ce qui est l'ID d'événement.

          Jusqu'à présent, ça semble fonctionner comme je m'y attends.

          -
          Edité par Ymox 24 décembre 2024 à 0:17:13

          • Partager sur Facebook
          • Partager sur Twitter
            23 décembre 2024 à 8:57:33

            J'ai donc retiré la boucle:

                // $maxExecutionTime = 110;
                // $startTime = time();
                $checkInterval = 3;
            
                // while (true) {
                //     if (connection_aborted() || (time() - $startTime) >= $maxExecutionTime) {
                //         break;
                //     }
            
                ...
            
                sleep($checkInterval);
                // }
            
                // echo "event: close\n";
                // echo "data: {\"message\": \"Stream closed\"}\n\n";
                flush();
                exit(0);

            Et je n'ai plus de problèmes(celui du sujet dans le forum JS). Merci.

            Je me suis toujours dit que c'est la boucle qui rendait ça "EventSource" en faite, mais non.

            Edit: Je n'ai toujours pas la réponse au problème principal.

            >Pensez vous que je dois utiliser les SSE pour la communication sur mobile avec le serveur pour ce cas ?

            -
            Edité par Asmitta 23 décembre 2024 à 9:08:11

            • Partager sur Facebook
            • Partager sur Twitter
              24 décembre 2024 à 0:18:20

              J'aurais envie de dire que si le client n'a pas de données à envoyer autre que "qu'est-ce qui aurait déjà été envoyé", tu peux rester sur du SSE.

              • Partager sur Facebook
              • Partager sur Twitter
                24 décembre 2024 à 18:55:34

                Merci.

                PS: C'est bon ici mais je suis entrain de tester les Web Sockets (j'ai fait mon choix mais je teste un peu tout, je n'aurai peut-être pas cette occasion encore) et je suis perdu : Configurer les WebSockets sur un site wordpress par Asmitta - page 1 - OpenClassrooms

                • Partager sur Facebook
                • Partager sur Twitter

                SSE vs Long-Polling

                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                • Editeur
                • Markdown