Wszystko,
HTML5 Rocks ma fajny samouczek dla początkujących na temat zdarzeń wysyłanych przez serwer (SSE):
http://www.html5rocks.com/en/tutorials/eventsource/basics/
Ale nie rozumiem ważnej koncepcji - co wyzwala zdarzenie na serwerze, które powoduje wysłanie wiadomości?
Innymi słowy - w przykładzie HTML5 - serwer po prostu wysyła raz znacznik czasu :
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
function sendMsg($id, $msg) {
echo "id: $id" . PHP_EOL;
echo "data: $msg" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
}
$serverTime = time();
sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
Gdybym budował praktyczny przykład - np. „Ścianę” w stylu Facebooka lub giełdę, w której serwer „wysyłałby” nową wiadomość do klienta za każdym razem, gdy zmieniają się dane, jak to działa?
Innymi słowy ... Czy skrypt PHP ma pętlę, która działa w sposób ciągły, sprawdzając zmiany w danych, a następnie wysyłając wiadomość za każdym razem, gdy ją znajdzie? Jeśli tak - skąd wiesz, kiedy zakończyć ten proces?
Lub - czy skrypt PHP po prostu wysyła wiadomość, a następnie kończy (jak wygląda to w przykładzie z HTML5Rocks)? Jeśli tak - w jaki sposób otrzymujesz ciągłe aktualizacje? Czy przeglądarka po prostu odpytuje stronę PHP w regularnych odstępach czasu? Jeśli tak - w jaki sposób to „zdarzenie wysłane przez serwer”? Czym różni się to od pisania funkcji setInterval w JavaScript, która używa AJAX do wywoływania strony PHP w regularnych odstępach czasu?
Przepraszam - to prawdopodobnie niesamowicie naiwne pytanie. Ale żaden z przykładów, które udało mi się znaleźć, nie wyjaśnia tego jasno.
[AKTUALIZACJA]
Myślę, że moje pytanie było źle sformułowane, więc oto kilka wyjaśnień.
Załóżmy, że mam stronę internetową, na której powinna być wyświetlana najnowsza cena akcji Apple.
Gdy użytkownik po raz pierwszy otwiera stronę, strona tworzy EventSource z adresem URL mojego „strumienia”.
var source = new EventSource('stream.php');
Moje pytanie brzmi: jak powinien działać plik „stream.php”?
Lubię to? (pseudo kod):
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
function sendMsg($msg) {
echo "data: $msg" . PHP_EOL;
echo PHP_EOL;
flush();
}
while (some condition) {
// check whether Apple's stock price has changed
// e.g., by querying a database, or calling a web service
// if it HAS changed, sendMsg with new price to client
// otherwise, do nothing (until next loop)
sleep (n) // wait n seconds until checking again
}
?>
Innymi słowy - czy „stream.php” pozostaje otwarty tak długo, jak długo klient jest z nim „połączony”?
Jeśli tak - czy to oznacza, że masz uruchomionych tyle wątków, stream.php
ile masz jednocześnie użytkowników? Jeśli tak - czy jest to wykonalne zdalnie, czy też jest to właściwy sposób tworzenia aplikacji? A skąd wiesz, kiedy możesz ZAKOŃCZYĆ wystąpienie stream.php
?
Mam naiwne wrażenie, że jeśli tak jest, PHP nie jest odpowiednią technologią dla tego rodzaju serwera. Ale wszystkie dema, które widziałem do tej pory, sugerują, że PHP jest do tego w porządku, dlatego jestem tak zdezorientowany ...
źródło
EventSource('stream.php')
, klient otwiera połączenie,stream.php
które przypomina wywołanie go przez ajax. TO połączenie uruchamia kod po stronie serwera i utrzymuje połączenie otwarte, o ile kod po stronie serwera ma coś do powiedzenia. Następnie połączenie zostaje zamknięte i po krótkim opóźnieniu (myślę, że 3 sekundy w chrome) klient ponownie otwiera połączenie, którestream.php
ponownie uruchamia plik.Odpowiedzi:
Tak, a twój pseudokod to rozsądne podejście.
W najbardziej typowym przypadku dzieje się tak, gdy użytkownik opuszcza witrynę. (Apache rozpoznaje zamknięte gniazdo i zabija instancję PHP.) Główny czas, w którym możesz zamknąć gniazdo po stronie serwera, to wtedy, gdy wiesz, że przez chwilę nie będzie żadnych danych; ostatnia wiadomość, jaką wysyłasz klientowi, to nakazanie mu powrotu o określonej godzinie. Na przykład w przypadku przesyłania strumieniowego akcji możesz zamknąć połączenie o 20:00 i powiedzieć klientom, aby wrócili za 8 godzin (zakładając, że NASDAQ jest otwarty na wyceny od 4:00 do 20:00). W piątek wieczorem każesz im wrócić w poniedziałek rano. (Mam nadchodzącą książkę o SSE i poświęcę kilka sekcji na ten temat.)
Cóż, ludzie twierdzą, że PHP nie jest odpowiednią technologią dla normalnych witryn internetowych, i mają rację: można to zrobić przy znacznie mniejszej ilości pamięci i cykli procesora, gdyby zastąpić cały stos LAMP C ++. Jednak mimo to PHP obsługuje większość witryn. Jest to bardzo produktywny język do pracy w sieci, ze względu na połączenie znanej składni podobnej do języka C i tak wielu bibliotek, a także wygodny dla menedżerów, jak wielu programistów PHP do wynajęcia, mnóstwo książek i innych zasobów oraz kilka dużych przypadki użycia (np. Facebook i Wikipedia). Są to w zasadzie te same powody, dla których możesz wybrać PHP jako technologię przesyłania strumieniowego.
Typowa konfiguracja nie będzie obejmowała jednego połączenia z NASDAQ na instancję PHP. Zamiast tego będziesz mieć inny proces z pojedynczym połączeniem z NASDAQ lub być może jednym połączeniem z każdej maszyny w klastrze do NASDAQ. To następnie wypycha ceny do serwera SQL / NoSQL lub do pamięci współdzielonej. Następnie PHP po prostu sonduje tę pamięć współdzieloną (lub bazę danych) i wypycha dane. Możesz też mieć serwer gromadzący dane, a każda instancja PHP otwiera połączenie przez gniazdo z tym serwerem. Serwer gromadzący dane wysyła aktualizacje do każdego ze swoich klientów PHP, gdy je otrzymuje, a oni z kolei wysyłają te dane do swojego klienta.
Głównym problemem związanym ze skalowalnością przy używaniu Apache + PHP do przesyłania strumieniowego jest pamięć dla każdego procesu Apache. Po osiągnięciu limitu pamięci sprzętu podejmij decyzję biznesową o dodaniu kolejnej maszyny do klastra lub wyłącz Apache z pętli i napisz dedykowany serwer HTTP. To ostatnie można zrobić w PHP, dzięki czemu cała istniejąca wiedza i kod mogą zostać ponownie wykorzystane lub możesz przepisać całą aplikację w innym języku. Czysty programista we mnie napisałby dedykowany, usprawniony serwer HTTP w C ++. Menedżer we mnie dodałby kolejne pudełko.
źródło
Zdarzenia wysyłane przez serwer służą do aktualizacji w czasie rzeczywistym od strony serwera do klienta. W pierwszym przykładzie połączenie z serwera nie jest utrzymywane, a klient próbuje połączyć się ponownie co 3 sekundy i sprawia, że zdarzenia wysyłane przez serwer nie różnią się od odpytywania ajax.
Tak więc, aby utrzymać połączenie, musisz zawinąć kod w pętlę i stale sprawdzać dostępność aktualizacji.
PHP jest oparte na wątkach i więcej podłączonych użytkowników spowoduje, że na serwerze zabraknie zasobów. Można to rozwiązać kontrolując czas wykonywania skryptu i kończąc skrypt, gdy przekroczy on określony czas (np. 10 minut).
EventSource
API automatycznie ponownie połączyć tak opóźnienie w dopuszczalnym zakresie.Sprawdź również moją bibliotekę PHP pod kątem zdarzeń wysyłanych przez serwer , możesz dowiedzieć się więcej o tym, jak wykonywać zdarzenia wysyłane z serwera w PHP i ułatwić kodowanie.
źródło
Zauważyłem, że sse techink wysyła co kilka opóźnień do klienta (coś w rodzaju odwrócenia łącza technicznego puli danych ze strony klienta ex Ajax pulowania danych). Aby rozwiązać ten problem, zrobiłem to na stronie sseServer.php:
<?php session_start(); header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); // recommended to prevent caching of event data require 'sse.php'; if ($_POST['message'] != ""){ $_SESSION['message'] = $_POST['message']; $_SESSION['serverTime'] = time(); } sendMsg($_SESSION['serverTime'], $_SESSION['message'] ); ?>
a sse.php to:
<?php function sendMsg($id, $msg) { echo "id: $id" . PHP_EOL; echo "data: $msg" . PHP_EOL; echo PHP_EOL; ob_flush(); flush(); } ?>
Zwróć uwagę, że w sseSerer.php uruchamiam sesję i używam zmiennej sesji! aby przezwyciężyć problem.
Wywołuję również sseServer.php przez Ajax (wysyłanie i ustawianie wartości
variable message
) za każdym razem, gdy chcę zaktualizować wiadomość.Teraz w jQuery (javascript) robię coś takiego: 1.) deklaruję zmienną globalną var timeStamp = 0; 2nd) używam następnego algorytmu:
if(typeof(EventSource)!=="undefined"){ var source=new EventSource("sseServer.php"); source.onmessage=function(event) if ((timeStamp!=event.lastEventId) && (timeStamp!=0)){ /* this is initialization */ timeStamp=event.lastEventId; $.notify("Please refresh "+event.data, "info"); } else { if (timeStamp==0){ timeStamp=event.lastEventId; } } /* fi */ } else { document.getElementById("result").innerHTML="Sorry, your browser does not support server-sent events..."; } /* fi */
W wierszu:
$.notify("Please refresh "+event.data, "info");
czy możesz obsłużyć wiadomość.W moim przypadku wysyłałem powiadomienie jQuery.
Możesz zamiast tego użyć POSIX PIPES lub tabeli DB, aby przekazać "wiadomość" przez POST, ponieważ sseServer.php robi coś w rodzaju "nieskończonej pętli".
Mój problem w tym czasie polega na tym, że powyższy kod NIE WYSYŁA "wiadomości" do wszystkich klientów, ale tylko do pary (klient, który wywołał sseServer.php, działa indywidualnie dla każdej pary), więc zmienię technika i Aktualizacja bazy danych ze strony, na której chcę wyzwolić "wiadomość", a następnie sseServer.php zamiast tego, aby uzyskać wiadomość przez POST, otrzyma ją z tabeli DB.
Mam nadzieję, że mam pomoc!
źródło
To jest naprawdę strukturalne pytanie dotyczące twojej aplikacji. Zdarzenia w czasie rzeczywistym to coś, o czym chcesz myśleć od samego początku, abyś mógł wokół nich zaprojektować swoją aplikację. Jeśli napisałeś aplikację, która po prostu uruchamia kilka losowych
mysql(i)_query
metod przy użyciu zapytań łańcuchowych i nie przekazuje ich przez żaden rodzaj pośrednika, to wiele razy nie będziesz miał wyboru i musisz albo przepisać znaczną część aplikacji, albo zrobić stałe sondowanie po stronie serwera.Jeśli jednak zarządzasz swoimi bytami jak obiektami i przepuszczasz je przez jakąś klasę pośredniczącą, możesz podłączyć się do tego procesu. Spójrz na ten przykład:
<?php class MyQueryManager { public function find($myObject, $objectId) { // Issue a select query against the database to get this object } public function save($myObject) { // Issue a query that saves the object to the database // Fire a new "save" event for the type of object passed to this method } public function delete($myObject) { // Fire a "delete" event for the type of object } }
W swojej aplikacji, gdy jesteś gotowy do zapisania:
<?php $someObject = $queryManager->find("MyObjectName", 1); $someObject->setDateTimeUpdated(time()); $queryManager->save($someObject);
Nie jest to najwspanialszy przykład, ale powinien służyć jako porządny element konstrukcyjny. Możesz podłączyć się do rzeczywistej warstwy trwałości, aby obsłużyć wyzwalanie tych zdarzeń. Następnie otrzymujesz je natychmiast (w czasie rzeczywistym, jak to tylko możliwe) bez wbijania serwera (ponieważ nie musisz ciągle wysyłać zapytań do bazy danych i sprawdzać, czy coś się zmieniło).
Oczywiście nie wyłapiesz w ten sposób ręcznych zmian w bazie danych - ale jeśli robisz coś ręcznie w bazie danych z jakąkolwiek częstotliwością, powinieneś:
źródło
Zasadniczo PHP nie jest odpowiednią technologią do tego typu rzeczy. Tak, możesz sprawić, że to zadziała, ale przy dużym obciążeniu będzie to katastrofa. Prowadzimy serwery giełdowe, które wysyłają sygnały o zmianie zapasów za pośrednictwem gniazd internetowych do dziesiątek tysięcy użytkowników - a gdybyśmy użyli do tego php ... Cóż, moglibyśmy, ale te domowe cykle - to tylko koszmar. Każde połączenie spowoduje oddzielny proces na serwerze lub będziesz musiał obsługiwać połączenia z jakiejś bazy danych.
Po prostu użyj nodejs i socket.io. Pozwoli ci to łatwo uruchomić i mieć działający serwer w ciągu kilku dni. Nodejs ma również swoje ograniczenia, ale w przypadku połączeń sieciowych (i SSE) jest to obecnie najpotężniejsza technologia.
A także - SSE nie jest takie dobre, jak się wydaje. Jedyną zaletą websockets jest to, że pakiety są natywne gzipowane (ws nie jest gzipowane), ale wadą jest to, że SSE jest połączeniem jednostronnym. Jeśli użytkownik chce dodać kolejny symbol giełdowy do subscripton, będzie musiał wysłać żądanie Ajax (w tym wszystkie problemy z kontrolą pochodzenia, a żądanie będzie wolne). W sieciach klient i serwer komunikują się w obie strony w jednym otwartym połączeniu, więc jeśli użytkownik wysyła sygnał transakcyjny lub zapisuje się do wyceny, po prostu wysyła ciąg w już otwartym połączeniu. I to szybko.
źródło