Jak wdrożyć podstawowe „długie odpytywanie”?

776

Mogę znaleźć wiele informacji o tym, jak działa Long Polling (na przykład to i to ), ale nie ma prostych przykładów, jak zaimplementować to w kodzie.

Wszystko, co mogę znaleźć, to cometd , który opiera się na frameworku Dojo JS i dość złożonym systemie serwerów.

Zasadniczo, w jaki sposób miałbym używać Apache do obsługi żądań i jak napisać prosty skrypt (powiedzmy w PHP), który „długo sprawdzałby” serwer pod kątem nowych wiadomości?

Przykład nie musi być skalowalny, bezpieczny ani kompletny, po prostu musi działać!

dbr
źródło

Odpowiedzi:

512

To prostsze niż początkowo myślałem. Zasadniczo masz stronę, która nic nie robi, dopóki dane, które chcesz wysłać, nie będą dostępne (powiedzmy, że przybywa nowa wiadomość).

Oto naprawdę prosty przykład, który wysyła prosty ciąg znaków po 2-10 sekundach. 1 na 3 szanse na zwrócenie błędu 404 (aby pokazać obsługę błędów w nadchodzącym przykładzie Javascript)

msgsrv.php

<?php
if(rand(1,3) == 1){
    /* Fake an error */
    header("HTTP/1.0 404 Not Found");
    die();
}

/* Send a string after a random number of seconds (2-10) */
sleep(rand(2,10));
echo("Hi! Have a random number: " . rand(1,10));
?>

Uwaga: w prawdziwej witrynie uruchomienie tego na zwykłym serwerze internetowym, takim jak Apache, spowoduje szybkie powiązanie wszystkich „wątków roboczych” i nie będzie w stanie odpowiedzieć na inne żądania. Można to zrobić na wiele sposobów, ale zaleca się napisanie „serwer długiego odpytywania” w czymś w rodzaju skręconego Pythona , który nie polega na jednym wątku na żądanie. cometD jest popularny (dostępny w kilku językach), a Tornado to nowa platforma stworzona specjalnie do takich zadań (została zbudowana dla długiego odpytywania FriendFeed) ... ale jako prosty przykład Apache jest więcej niż wystarczający ! Skrypt ten można łatwo napisać w dowolnym języku (wybrałem Apache / PHP, ponieważ są one bardzo popularne i zdarzyło mi się uruchamiać je lokalnie)

Następnie w Javascripcie żądasz powyższego pliku ( msg_srv.php) i czekasz na odpowiedź. Kiedy je otrzymasz, działasz na podstawie danych. Następnie żądasz pliku i czekasz ponownie, działasz na podstawie danych (i powtórz)

Poniżej znajduje się przykład takiej strony. Gdy strona zostanie załadowana, wysyła początkowe żądanie msgsrv.phppliku. Jeśli się powiedzie, dołączamy wiadomość do #messagesdiv, a następnie po 1 sekundzie ponownie wywołujemy funkcję waitForMsg, co powoduje oczekiwanie.

1 sekunda setTimeout()jest naprawdę podstawowym ogranicznikiem szybkości, bez niej działa dobrze, ale jeśli msgsrv.php zawsze wraca natychmiast (na przykład z błędem składni) - zalewasz przeglądarkę i może się szybko zawiesić. Lepiej byłoby to zrobić sprawdzając, czy plik zawiera prawidłową odpowiedź JSON i / lub utrzymując bieżącą liczbę żądań na minutę / sekundę i odpowiednio zatrzymując.

Jeśli strona popełni błąd, dołącza błąd do #messagesdiv, czeka 15 sekund, a następnie próbuje ponownie (identycznie, jak czekamy 1 sekundę po każdej wiadomości)

Zaletą tego podejścia jest to, że jest bardzo odporny. Jeśli połączenie internetowe klienta zostanie przerwane, nastąpi przekroczenie limitu czasu, a następnie spróbuj ponownie nawiązać połączenie - jest to nieodłączne od czasu działania odpytywania, nie jest wymagana skomplikowana obsługa błędów

W każdym razie long_poller.htmkod, używając frameworka jQuery:

<html>
<head>
    <title>BargePoller</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript" charset="utf-8"></script>

    <style type="text/css" media="screen">
      body{ background:#000;color:#fff;font-size:.9em; }
      .msg{ background:#aaa;padding:.2em; border-bottom:1px #000 solid}
      .old{ background-color:#246499;}
      .new{ background-color:#3B9957;}
    .error{ background-color:#992E36;}
    </style>

    <script type="text/javascript" charset="utf-8">
    function addmsg(type, msg){
        /* Simple helper to add a div.
        type is the name of a CSS class (old/new/error).
        msg is the contents of the div */
        $("#messages").append(
            "<div class='msg "+ type +"'>"+ msg +"</div>"
        );
    }

    function waitForMsg(){
        /* This requests the url "msgsrv.php"
        When it complete (or errors)*/
        $.ajax({
            type: "GET",
            url: "msgsrv.php",

            async: true, /* If set to non-async, browser shows page as "Loading.."*/
            cache: false,
            timeout:50000, /* Timeout in ms */

            success: function(data){ /* called when request to barge.php completes */
                addmsg("new", data); /* Add response to a .msg div (with the "new" class)*/
                setTimeout(
                    waitForMsg, /* Request next message */
                    1000 /* ..after 1 seconds */
                );
            },
            error: function(XMLHttpRequest, textStatus, errorThrown){
                addmsg("error", textStatus + " (" + errorThrown + ")");
                setTimeout(
                    waitForMsg, /* Try again after.. */
                    15000); /* milliseconds (15seconds) */
            }
        });
    };

    $(document).ready(function(){
        waitForMsg(); /* Start the inital request */
    });
    </script>
</head>
<body>
    <div id="messages">
        <div class="msg old">
            BargePoll message requester!
        </div>
    </div>
</body>
</html>
dbr
źródło
7
Czy niektóre wiadomości nie mogły się przedostać przy użyciu tego pomysłu? Powiedzmy, że w tym 1 sekundowym czasie wysłano 1000 wiadomości czatowych, skąd serwer wiedziałby, że może wysłać 1000 wiadomości specjalnie do tego klienta?
DevDevDev,
15
Prawdopodobnie. Jest to bardzo uproszczony przykład, aby zademonstrować tę koncepcję. Aby to zrobić lepiej, potrzebujesz bardziej skomplikowanego kodu po stronie serwera, w którym przechowałby 1000 wiadomości dla tego konkretnego klienta i wysłałby je w jednym kawałku. Możesz także bezpiecznie skrócić limit czasu
waitForMsg
21
nodejs to kolejne doskonałe rozwiązanie po stronie serwera do obsługi długich zapytań, z dodatkową zaletą (nad Twisted), że możesz pisać kod serwera również w JavaScript.
Husky,
8
To tylko zwykłe powtarzające się połączenia AJAX z serwerem w odstępie 1 sekundy. Nie ma to nic wspólnego z „długim odpytywaniem”. Długie odpytywanie powinno utrzymywać połączenie przy życiu, o ile naliczony zostanie limit czasu klienta.
Deele
6
pytanie brzmi, co robi prawdziwy skrypt PHP zamiast sleep(rand(2,10));? aby nic nie robić, sonduj bazę danych co 100 milisekund? kiedy decyduje się umrzeć?
Luis Siquot,
41

Mam naprawdę prosty przykład czatu jako część slosh .

Edycja : (ponieważ wszyscy wklejają tutaj swój kod)

Jest to kompletny czat dla wielu użytkowników oparty na JSON, wykorzystujący długie odpytywanie i slosh . To jest demonstracja wykonywania połączeń, więc zignoruj ​​problemy XSS. Nikt nie powinien tego wdrażać bez uprzedniej dezynfekcji.

Zauważ, że klient zawsze ma połączenie z serwerem i gdy tylko ktoś wyśle ​​wiadomość, wszyscy powinni ją zobaczyć z grubsza natychmiast.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- Copyright (c) 2008 Dustin Sallings <[email protected]> -->
<html lang="en">
  <head>
    <title>slosh chat</title>
    <script type="text/javascript"
      src="http://code.jquery.com/jquery-latest.js"></script>
    <link title="Default" rel="stylesheet" media="screen" href="style.css" />
  </head>

  <body>
    <h1>Welcome to Slosh Chat</h1>

    <div id="messages">
      <div>
        <span class="from">First!:</span>
        <span class="msg">Welcome to chat. Please don't hurt each other.</span>
      </div>
    </div>

    <form method="post" action="#">
      <div>Nick: <input id='from' type="text" name="from"/></div>
      <div>Message:</div>
      <div><textarea id='msg' name="msg"></textarea></div>
      <div><input type="submit" value="Say it" id="submit"/></div>
    </form>

    <script type="text/javascript">
      function gotData(json, st) {
        var msgs=$('#messages');
        $.each(json.res, function(idx, p) {
          var from = p.from[0]
          var msg = p.msg[0]
          msgs.append("<div><span class='from'>" + from + ":</span>" +
            " <span class='msg'>" + msg + "</span></div>");
        });
        // The jQuery wrapped msgs above does not work here.
        var msgs=document.getElementById("messages");
        msgs.scrollTop = msgs.scrollHeight;
      }

      function getNewComments() {
        $.getJSON('/topics/chat.json', gotData);
      }

      $(document).ready(function() {
        $(document).ajaxStop(getNewComments);
        $("form").submit(function() {
          $.post('/topics/chat', $('form').serialize());
          return false;
        });
        getNewComments();
      });
    </script>
  </body>
</html>
Dustin
źródło
1
Czy mogę wiedzieć, jak to się zawsze łączy? Przepraszam, jeśli pytam o coś głupiego, ale chcę o tym wiedzieć.
Rocky Singh
4
Wykonuje HTTP GET, a serwer blokuje GET, dopóki nie będą dostępne dane. Gdy dane docierają do serwera, serwer zwraca je do klienta, kolejkuje cokolwiek innego, co mogłoby się pojawić, a następnie klient łączy się ponownie i odbiera brakujące wiadomości, jeśli takie istnieją, w przeciwnym razie blokuje się ponownie.
Dustin
4
Na początku może nie być oczywiste, ale rzecz w tym, że „zawsze połączonym stanem” jest ajaxStop z getNewCommentstam wywołaniem zwrotnym, więc po prostu odpala go na końcu każdego żądania ajax
baldrs
32

Tornado jest przeznaczony do długiego odpytywania i zawiera bardzo minimalną (kilkaset linii Pythona) aplikację do czatowania w / Examples / chatdemo , w tym kod serwera i kod klienta JS. Działa to tak:

  • Klienci używają JS do żądania aktualizacji, ponieważ (numer ostatniej wiadomości), URLHandler serwera odbiera je i dodaje wywołanie zwrotne, aby odpowiedzieć klientowi w kolejce.

  • Gdy serwer otrzymuje nową wiadomość, zdarzenie onmessage jest uruchamiane, zapętla wywołania zwrotne i wysyła wiadomości.

  • JS po stronie klienta odbiera komunikat, dodaje go do strony, a następnie prosi o aktualizacje, ponieważ ten nowy identyfikator wiadomości.

mikemaccana
źródło
25

Myślę, że klient wygląda jak normalne asynchroniczne żądanie AJAX, ale oczekuje się, że powrót zajmie „dużo czasu”.

Serwer wygląda następująco.

while (!hasNewData())
    usleep(50);

outputNewData();

Tak więc żądanie AJAX trafia do serwera, prawdopodobnie wraz ze znacznikiem czasu, kiedy była ostatnia aktualizacja, hasNewData()abyś wiedział, jakie dane już masz. Serwer następnie śpi w pętli, dopóki nowe dane nie będą dostępne. Przez cały czas twoje żądanie AJAX jest nadal połączone, po prostu czeka tam na dane. Wreszcie, gdy nowe dane są dostępne, serwer przekazuje je do żądania AJAX i zamyka połączenie.

Greg
źródło
10
To zajęte oczekiwanie, które blokuje bieżący wątek. To wcale nie jest skalowane.
Wouter Lievens
10
Nie, zaspanie nie jest zajęte. Cały sens „czekania” polega na tym, by na jakiś czas zablokować twój wątek. Prawdopodobnie miał na myśli 50 milisekund (usleep (50000)), a nie 50 mikrosekund! Ale w każdym razie, przy typowej konfiguracji Apache / PHP, czy jest na to inny sposób?
Matt
Cóż, z założenia nie można wykonać funkcji blokowania wiadomości czatu bez czekania.
Tomáš Zato - Przywróć Monikę
Świetnie, naprawdę! Zbudowałem funkcję rekurencyjną na serwerze, aby sprawdzić nowe dane. Ale jaki jest najlepszy produkt do efektywnego wykorzystania długiego odpytywania? Używam normalnego Apache, a serwer nie reaguje, gdy otwieram więcej niż 4/5 kart przeglądarki :( Szukam czegoś do użycia z PHP
współczesne
17

Oto kilka klas, których używam do długiego odpytywania w C #. Jest w zasadzie 6 klas (patrz poniżej).

  1. Kontroler : Przetwarza czynności wymagane do utworzenia prawidłowej odpowiedzi (operacje bazy danych itp.)
  2. Procesor : zarządza komunikacją asynchroniczną ze stroną internetową (samą)
  3. IAsynchProcessor : usługa przetwarza instancje, które implementują ten interfejs
  4. Sevice : Przetwarza obiekty żądań, które implementują IAsynchProcessor
  5. Żądanie : opakowanie IAsynchProcessor zawierające twoją odpowiedź (obiekt)
  6. Odpowiedź : Zawiera niestandardowe obiekty lub pola
Więzień ZERO
źródło
2
OK ... więc DLACZEGO to zostało odrzucone? Te klasy są rzeczywiście poprawnymi przykładami długiego odpytywania.
Więzień ZERO
Prawdziwe długie odpytywanie nie jest (po prostu) praktyką zwiększania interwału, w którym przeprowadzasz normalną ankietę (na zasobie). Jest to część większego wzorca ... który jest „nieco” przedmiotem interpretacji… ale tylko w niektórych obszarach ogólnego wdrożenia. To powiedziawszy ... te klasy są zgodne z tym wzorem! Więc jeśli masz powód, aby głosować w dół ... Naprawdę byłbym zainteresowany tym powodem.
Więzień ZERO
Być może został odrzucony, ponieważ nie odnosi się bezpośrednio do pytania o prosty przykład kodu. Oczywiście nie głosowałem, więc mogę tylko zgadywać.
Andrew
16

To jest fajny 5-minutowy screencast na temat długiego odpytywania za pomocą PHP i jQuery: http://screenr.com/SNH

Kod jest dość podobny do powyższego przykładu dbr .

Sean O
źródło
3
Myślę, że powinieneś postrzegać to jedynie jako wprowadzenie do długiego odpytywania, ponieważ ta implementacja z pewnością zabije twój serwer z wieloma współbieżnymi użytkownikami.
Alfred
dopiero się uczę o tym wszystkim ... jak to jest niezawodne, czy nie, z kilkoma użytkownikami ... powiedz, że 10 czatuje?
kiedyś
12

Oto prosty przykład długiego odpytywania w PHP autorstwa Erika Dubbelboera przy użyciu Content-type: multipart/x-mixed-replacenagłówka:

<?

header('Content-type: multipart/x-mixed-replace; boundary=endofsection');

// Keep in mind that the empty line is important to separate the headers
// from the content.
echo 'Content-type: text/plain

After 5 seconds this will go away and a cat will appear...
--endofsection
';
flush(); // Don't forget to flush the content to the browser.


sleep(5);


echo 'Content-type: image/jpg

';

$stream = fopen('cat.jpg', 'rb');
fpassthru($stream);
fclose($stream);

echo '
--endofsection
';

A oto demo:

http://dubbelboer.com/multipart.php

Jasdeep Khalsa
źródło
11

Użyłem tego, aby poznać Comet, skonfigurowałem również Comet za pomocą serwera Java Glassfish i znalazłem wiele innych przykładów, subskrybując cometdaily.com

Adam
źródło
9

Poniżej znajduje się długie rozwiązanie ankietowe, które opracowałem dla Inform8 Web. Zasadniczo przesłonisz klasę i zaimplementujesz metodę loadData. Kiedy loadData zwróci wartość lub limit czasu operacji wydrukuje wynik i zwróci.

Jeśli przetwarzanie skryptu może potrwać dłużej niż 30 sekund, może być konieczne zmodyfikowanie wywołania set_time_limit () na coś dłuższego.

Licencja Apache 2.0. Najnowsza wersja na github https://github.com/ryanhend/Inform8/blob/master/Inform8-web/src/config/lib/Inform8/longpoll/LongPoller.php

Ryan

abstract class LongPoller {

  protected $sleepTime = 5;
  protected $timeoutTime = 30;

  function __construct() {
  }


  function setTimeout($timeout) {
    $this->timeoutTime = $timeout;
  }

  function setSleep($sleep) {
    $this->sleepTime = $sleepTime;
  }


  public function run() {
    $data = NULL;
    $timeout = 0;

    set_time_limit($this->timeoutTime + $this->sleepTime + 15);

    //Query database for data
    while($data == NULL && $timeout < $this->timeoutTime) {
      $data = $this->loadData();
      if($data == NULL){

        //No new orders, flush to notify php still alive
        flush();

        //Wait for new Messages
        sleep($this->sleepTime);
        $timeout += $this->sleepTime;
      }else{
        echo $data;
        flush();
      }
    }

  }


  protected abstract function loadData();

}
Ryan Henderson
źródło
8

Dzięki za kod, dbr . Tylko mała literówka w long_poller.htm wokół linii

1000 /* ..after 1 seconds */

Myślę, że tak powinno być

"1000"); /* ..after 1 seconds */

aby działało.

Dla zainteresowanych wypróbowałem ekwiwalent Django. Rozpocznij nowy projekt Django, powiedz lp dla długiego odpytywania:

django-admin.py startproject lp

Zadzwoń do aplikacji msgsrv dla serwera wiadomości:

python manage.py startapp msgsrv

Dodaj następujące wiersze do settings.py, aby mieć katalog szablonów :

import os.path
PROJECT_DIR = os.path.dirname(__file__)
TEMPLATE_DIRS = (
    os.path.join(PROJECT_DIR, 'templates'),
)

Zdefiniuj wzorce adresów URL w urls.py jako takie:

from django.views.generic.simple import direct_to_template
from lp.msgsrv.views import retmsg

urlpatterns = patterns('',
    (r'^msgsrv\.php$', retmsg),
    (r'^long_poller\.htm$', direct_to_template, {'template': 'long_poller.htm'}),
)

Plik msgsrv / views.py powinien wyglądać następująco:

from random import randint
from time import sleep
from django.http import HttpResponse, HttpResponseNotFound

def retmsg(request):
    if randint(1,3) == 1:
        return HttpResponseNotFound('<h1>Page not found</h1>')
    else:
        sleep(randint(2,10))
        return HttpResponse('Hi! Have a random number: %s' % str(randint(1,10)))

Wreszcie, szablony / long_poller.htm powinny być takie same jak powyżej z poprawioną literówką. Mam nadzieję że to pomoże.

Xoblau
źródło
W rzeczywistości "15000"jest to błąd składniowy. setTimeout przyjmuje liczbę całkowitą jako swój drugi parametr.
Andrew Hedges
Ta odpowiedź wymaga pracy. Jest zwieńczeniem jednego lub więcej komentarzy i osobnej odpowiedzi lub odpowiedzi.
Brian Webster,
8

Jest to jeden ze scenariuszy, dla których PHP jest bardzo złym wyborem. Jak wspomniano wcześniej, możesz bardzo szybko powiązać wszystkich pracowników Apache, robiąc coś takiego. PHP jest zbudowany do uruchamiania, wykonywania, zatrzymywania. Nie jest zbudowany na początek, poczekaj ... uruchom, zatrzymaj. Szybko zepsujesz serwer i przekonasz się, że masz niesamowite problemy ze skalowaniem.

To powiedziawszy, nadal możesz to zrobić za pomocą PHP i nie zabijaj swojego serwera za pomocą nginx HttpPushStreamModule: http://wiki.nginx.org/HttpPushStreamModule

Instalujesz nginx przed Apache (lub czymkolwiek innym), a on zajmie się utrzymywaniem otwartych równoczesnych połączeń. Po prostu odpowiadasz z ładunkiem, wysyłając dane na wewnętrzny adres, co możesz zrobić z zadaniem w tle lub po prostu wysyłać wiadomości do osób, które czekały za każdym razem, gdy nadejdą nowe żądania. Dzięki temu procesy PHP nie będą otwarte podczas długiego odpytywania.

Nie dotyczy to wyłącznie PHP i można to zrobić przy użyciu nginx z dowolnym językiem zaplecza. Obciążenie równoczesnych otwartych połączeń jest równe Node.js, więc największym atutem jest to, że wyciąga cię z POTRZEBUJĄCEGO węzła dla czegoś takiego.

Widzisz wiele innych osób, które wspominają o innych bibliotekach językowych w celu przeprowadzenia długiego odpytywania, i to nie bez powodu. PHP po prostu nie jest dobrze zbudowane do tego rodzaju zachowań.

Brightball
źródło
Czy to problem Apache czy PHP? Czy miałbym problemy z długim odpytywaniem, gdyby mój kod PHP działał bezpośrednio na nginx lub lighttpd?
David,
To mniej problem z PHP, a więcej niewłaściwe użycie PHP. Na każde żądanie PHP uruchamia skrypt od zera, ładując biblioteki w razie potrzeby, wykonując swój kod, a następnie zamykając się podczas odśmiecania wszystkiego, co rozpoczęło się w żądaniu. Przez lata wprowadzono wiele zmian w PHP, aby zminimalizować wpływ, takich jak późne wiązania statyczne, leniwe ładowanie, pamięci podręczne kodów bajtowych w celu usunięcia I / O dysku itp. Problemem jest to, że PHP ma się uruchamiać i zatrzymywać tak szybko jak to możliwe. Języki, które zostaną załadowane raz / boot i otworzą wątek dla żądania, są znacznie lepiej dostosowane do długiego odpytywania.
brightball
Ale aby odpowiedzieć na pytanie, tak, problem występuje niezależnie od tego, czy używasz Apache, czy czegoś innego. Właśnie tak działa PHP. Powinienem to zmienić, aby powiedzieć, że jeśli masz mieć znane maksymalne obciążenie ruchem, PHP będzie w porządku. Widziałem systemy wbudowane wykorzystujące PHP, które nie mają problemów, ponieważ istnieje tylko kilka połączeń. Potencjalnie może to być znośne w firmowym intranecie. Jednak w przypadku aplikacji publicznych całkowicie zabijesz swoje serwery w miarę wzrostu ruchu.
brightball
4

Dlaczego nie rozważyć gniazd sieciowych zamiast długiego odpytywania? Są bardzo wydajne i łatwe w konfiguracji. Są one jednak obsługiwane tylko w nowoczesnych przeglądarkach. Oto krótkie odniesienie .

shasi kanth
źródło
Myślę, że gdy websockets zostaną wdrożone wszędzie (prawdopodobnie nie w najbliższych latach), staną się standardem dla tego rodzaju aplikacji. Niestety na razie nie możemy na nich polegać w przypadku aplikacji produkcyjnych.
Richard
3
@Richard Możesz jednak użyć czegoś takiego jak Socket.IO, który zapewnia automatyczne transfery awaryjne, zapewniając funkcjonalność podobną do gniazd sieciowych aż do IE 6.
Brad
3

Grupa WS-I opublikowała coś o nazwie „Reliable Secure Profile”, który ma implementację Glass Fish i .NET, które najwyraźniej dobrze ze sobą współpracują .

Przy odrobinie szczęścia istnieje również implementacja Javascript .

Istnieje również implementacja Silverlight, która wykorzystuje HTTP Duplex. Możesz podłączyć javascript do obiektu Silverlight, aby uzyskać wywołania zwrotne, gdy nastąpi wypychanie.

Istnieją również komercyjne wersje płatne .

goodguys_activate
źródło
2

Możesz wypróbować icomet ( https://github.com/ideawu/icomet ), serwer komet C1000K C ++ zbudowany z libevent. icomet zapewnia również bibliotekę JavaScript, jest łatwy w użyciu tak prosty jak

var comet = new iComet({
    sign_url: 'http://' + app_host + '/sign?obj=' + obj,
    sub_url: 'http://' + icomet_host + '/sub',
    callback: function(msg){
        // on server push
        alert(msg.content);
    }
});

icomet obsługuje wiele przeglądarek i systemów operacyjnych, w tym Safari (iOS, Mac), IE (Windows), Firefox, Chrome itp.

ideawu
źródło
0

Najprostszy NodeJS

const http = require('http');

const server = http.createServer((req, res) => {
  SomeVeryLongAction(res);
});

server.on('clientError', (err, socket) => {
  socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});

server.listen(8000);

// the long running task - simplified to setTimeout here
// but can be async, wait from websocket service - whatever really
function SomeVeryLongAction(response) {
  setTimeout(response.end, 10000);
}

Scenariusz mądry pod względem produkcyjnym w programie Express, na przykład responsew oprogramowaniu pośrednim. Czy robisz to, co musisz zrobić, możesz objąć wszystkie długie ankietowane metody mapowaniem lub czymś (co jest widoczne dla innych przepływów) i wywoływać, <Response> response.end()gdy będziesz gotowy. Nie ma nic specjalnego w długo odpytywanych połączeniach. Reszta to sposób, w jaki zwykle tworzysz strukturę aplikacji.

Jeśli nie wiesz, co mam na myśli, określając zakres, powinno to dać ci pomysł

const http = require('http');
var responsesArray = [];

const server = http.createServer((req, res) => {
  // not dealing with connection
  // put it on stack (array in this case)
  responsesArray.push(res);
  // end this is where normal api flow ends
});

server.on('clientError', (err, socket) => {
  socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});

// and eventually when we are ready to resolve
// that if is there just to ensure you actually 
// called endpoint before the timeout kicks in
function SomeVeryLongAction() {
  if ( responsesArray.length ) {
    let localResponse = responsesArray.shift();
    localResponse.end();
  }
}

// simulate some action out of endpoint flow
setTimeout(SomeVeryLongAction, 10000);
server.listen(8000);

Jak widzisz, możesz naprawdę reagować na wszystkie połączenia, po pierwsze, rób co chcesz. Jest idna każde żądanie, więc powinieneś być w stanie korzystać z mapy i uzyskać dostęp do konkretnego połączenia poza interfejsem API.

sp3c1
źródło