support - Eventos enviados por el servidor y php: ¿qué desencadena eventos en el servidor?
server sent events php (5)
Todas,
HTML5 Rocks tiene un buen tutorial para principiantes sobre eventos enviados por el servidor (SSE):
http://www.html5rocks.com/en/tutorials/eventsource/basics/
Pero, no entiendo un concepto importante: ¿qué desencadena el evento en el servidor que hace que se envíe un mensaje?
En otras palabras, en el ejemplo de HTML5, el servidor simplemente envía una marca de tiempo una vez :
<?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()));
Si estuviera construyendo un ejemplo práctico, por ejemplo, un "muro" al estilo de Facebook o un código de acciones, en el que el servidor "empujara" un nuevo mensaje al cliente cada vez que cambiara algún dato, ¿cómo funciona eso?
En otras palabras ... ¿El script PHP tiene un bucle que se ejecuta continuamente, verificando un cambio en los datos y luego enviando un mensaje cada vez que encuentra uno? Si es así, ¿cómo sabe cuándo finalizar ese proceso?
O bien, ¿el script PHP simplemente envía el mensaje y luego termina (como parece ser el caso en el ejemplo de HTML5Rocks)? Si es así, ¿cómo se obtienen actualizaciones continuas? ¿El navegador simplemente está sondeando la página de PHP a intervalos regulares? Si es así, ¿cómo es eso un "evento enviado por el servidor"? ¿En qué se diferencia esto de escribir una función setInterval en JavaScript que usa AJAX para llamar a una página PHP en un intervalo regular?
Lo siento, esta es probablemente una pregunta increíblemente ingenua. Pero ninguno de los ejemplos que he podido encontrar lo aclara.
[ACTUALIZAR]
Creo que mi pregunta estaba mal redactada, así que aquí hay algunas aclaraciones.
Digamos que tengo una página web que debe mostrar el precio más reciente de las acciones de Apple.
Cuando el usuario abre la página por primera vez, la página crea un EventSource con la URL de mi "secuencia".
var source = new EventSource(''stream.php'');
Mi pregunta es esta: ¿cómo debería funcionar "stream.php"?
¿Me gusta esto? (pseudo-código):
<?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
}
?>
En otras palabras, ¿"stream.php" permanece abierto mientras el cliente esté "conectado" a él?
En caso afirmativo, ¿significa eso que tienes tantos hilos ejecutando stream.php
como usuarios concurrentes? Si es así, ¿es eso remotamente factible o una forma apropiada de construir una aplicación? ¿Y cómo sabes cuándo puedes END una instancia de stream.php
?
Mi impresión ingenua es que, si este es el caso, PHP no es una tecnología adecuada para este tipo de servidor. Pero todas las demos que he visto hasta ahora implican que PHP está bien para esto, y por eso estoy tan confundido ...
"... ¿Stream.php" permanece abierto mientras el cliente esté "conectado" a él? "
Sí, y su pseudocódigo es un enfoque razonable.
"¿Y cómo sabes cuándo puedes END una instancia de stream.php?"
En el caso más típico, esto sucede cuando el usuario abandona su sitio. (Apache reconoce el socket cerrado y mata la instancia de PHP). La hora principal en que puede cerrar el socket desde el servidor es si sabe que no habrá datos por un tiempo; el último mensaje que envía al cliente es para decirle que regrese en un momento determinado. Por ejemplo, en su caso de flujo de existencias, podría cerrar la conexión a las 8 p.m. e indicar a los clientes que regresen en 8 horas (suponiendo que NASDAQ esté abierto para cotizaciones de 4 a.m. a 8 p.m.). El viernes por la noche les dices que regresen el lunes por la mañana. (Tengo un próximo libro sobre SSE, y dedico un par de secciones sobre este tema).
"... si este es el caso, PHP no es una tecnología adecuada para este tipo de servidor. Pero todas las demostraciones que he visto hasta ahora implican que PHP está bien para esto, y por eso estoy tan confuso..."
Bueno, la gente argumenta que PHP no es una tecnología adecuada para sitios web normales, y tienen razón: podrías hacerlo con mucha menos memoria y ciclos de CPU si reemplazaras tu pila LAMP completa con C ++. Sin embargo, a pesar de esto, PHP impulsa la mayoría de los sitios que están muy bien. Es un lenguaje muy productivo para el trabajo en la web, debido a una combinación de una sintaxis similar a C y muchas bibliotecas, y una reconfortante para los administradores como muchos programadores de PHP para contratar, muchos libros y otros recursos, y algunos grandes casos de uso (por ejemplo, Facebook y Wikipedia). Esos son básicamente los mismos motivos por los que puede elegir PHP como su tecnología de transmisión.
La configuración típica no será una conexión con NASDAQ por instancia de PHP. En su lugar, tendrá otro proceso con una única conexión al NASDAQ, o tal vez una única conexión de cada equipo de su clúster al NASDAQ. Eso luego empuja los precios hacia un servidor SQL / NoSQL o hacia la memoria compartida. Luego PHP simplemente sondea esa memoria compartida (o base de datos) y saca los datos. O bien, tener un servidor de recopilación de datos, y cada instancia de PHP abre una conexión de socket a ese servidor. El servidor de recopilación de datos envía actualizaciones a cada uno de sus clientes de PHP, a medida que los recibe, y ellos a su vez envían esos datos a su cliente.
El principal problema de escalabilidad con el uso de Apache + PHP para la transmisión es la memoria de cada proceso de Apache. Cuando llegue al límite de memoria del hardware, tome la decisión comercial de agregar otra máquina al clúster o desconecte Apache del ciclo y escriba un servidor HTTP dedicado. Esto último se puede hacer en PHP para que todos sus conocimientos y códigos existentes puedan volver a utilizarse, o puede volver a escribir la aplicación completa en otro idioma. El desarrollador puro en mí escribiría un servidor HTTP dedicado y simplificado en C ++. El gerente en mí agregaría otra caja.
Básicamente, PHP no es una tecnología adecuada para este tipo de cosas. Sí, puedes hacerlo funcionar, pero será un desastre a gran altura. Ejecutamos servidores de stock que envían señales de cambio de stock a través de websockets a docenas de miles de usuarios, y si usáramos php para eso ... Bueno, podríamos, pero esos ciclos hechos en casa, es solo una pesadilla. Cada conexión individual hará un proceso separado en el servidor o tendrá que manejar conexiones de algún tipo de base de datos.
Simplemente use nodejs y socket.io. Le permitirá comenzar fácilmente y tener un servidor en ejecución en un par de días. Nodejs también tiene sus propias limitaciones, pero para las conexiones de websockets (y SSE) ahora es la tecnología más poderosa.
Y también - SSE no es tan bueno como parece. La única ventaja de los websockets es que los paquetes tienen gzip de forma nativa (ws no tiene gzip), pero a la baja es que SSE es una conexión de un solo lado. Su usuario, si desea agregar otro símbolo de stock al subscripto, tendrá que hacer una solicitud ajax (incluidos todos los problemas con el control de origen y la solicitud será lenta). En websockets, el cliente y el servidor se comunican en ambos sentidos en una sola conexión abierta, de modo que si el usuario envía una señal de trading o se suscribe para cotizar, simplemente envía una cadena en una conexión ya abierta. Y es rápido.
Esta es realmente una pregunta estructural sobre su aplicación. Los eventos en tiempo real son algo en lo que debes pensar desde el principio, para que puedas diseñar tu aplicación a su alrededor. Si ha escrito una aplicación que solo ejecuta un montón de métodos aleatorios mysql(i)_query
que utilizan consultas de cadena y no los pasa por ningún tipo de intermediario, muchas veces no tendrá más remedio que reescribir gran parte de su aplicación, o realice sondeos constantes del lado del servidor.
Sin embargo, si administra sus entidades como objetos y las pasa a través de algún tipo de clase intermedia, puede enganchar en ese proceso. Mira este ejemplo:
<?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
}
}
En su aplicación, cuando esté listo para guardar:
<?php
$someObject = $queryManager->find("MyObjectName", 1);
$someObject->setDateTimeUpdated(time());
$queryManager->save($someObject);
Este no es el ejemplo más elegante, pero debería servir como un bloque de construcción decente. Puede conectar su capa de persistencia real para controlar la activación de estos eventos. Luego los obtienes inmediatamente (en tiempo real como sea posible) sin golpear tu servidor (ya que no tienes que consultar constantemente tu base de datos y ver si las cosas han cambiado).
Obviamente no detectará cambios manuales en la base de datos de esta manera, pero si está haciendo algo manualmente en su base de datos con cualquier frecuencia, debe:
- Solucione el problema que requiere que tenga que realizar un cambio manual
- Cree una herramienta para acelerar el proceso y desencadene estos eventos
Los eventos enviados por el servidor son para la actualización en tiempo real desde el lado del servidor hasta el lado del cliente. En el primer ejemplo, la conexión del servidor no se mantiene y el cliente intenta conectarse de nuevo cada 3 segundos y hace que los eventos enviados por el servidor no tengan ninguna diferencia con el sondeo ajax.
Por lo tanto, para que la conexión persista, debe ajustar el código en un bucle y buscar actualizaciones constantemente.
PHP está basado en subprocesos y más usuarios conectados harán que el servidor se quede sin recursos. Esto puede resolverse controlando el tiempo de ejecución del script y finalizando el script cuando exceda una cantidad de tiempo (es decir, 10 minutos). La API de EventSource
se conectará automáticamente de nuevo, por lo que la demora se encuentra en un rango aceptable.
Además, consulte mi biblioteca PHP para ver los eventos enviados por el servidor , puede comprender más acerca de cómo hacer eventos enviados por el servidor en PHP y hacer que sea más fácil codificar.
Me he dado cuenta de que el tecnólogo sse envía cada dos datos de demora al cliente (algo así como revertir el interrogatorio de datos de agrupación desde la página de cliente ex agrupación de datos de Ajax). Para solucionar este problema hice esto en una página 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''] );
?>
y el sse.php es:
<?php
function sendMsg($id, $msg) {
echo "id: $id" . PHP_EOL;
echo "data: $msg" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
}
?>
¡Observe que en sseSerer.php comienzo una sesión y uso una variable de sesión! para superar el problema
También llamo sseServer.php a través de Ajax (publicando y estableciendo el valor en un variable message
) cada vez que quiero "actualizar" el mensaje.
Ahora en jQuery (javascript) hago algo como eso: 1 °) declaro una variable global var timeStamp = 0; 2º) uso el siguiente algoritmo:
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 */
En la línea de: $.notify("Please refresh "+event.data, "info");
¿Hay algo que pueda manejar el mensaje?
En mi caso solía enviar una notificación de jQuery.
Puede usar POSIX PIPES o DB Table en su lugar para pasar el "mensaje" a través de POST ya que sseServer.php hace algo así como un "ciclo infinito".
Mi problema en ese momento es que el código anterior NO ENVÍA el "mensaje" a todos los clientes, sino solo al par (el cliente que llamó al sseServer.php funciona como un individuo para cada par) así que cambiaré la técnica y a un Actualización de base de datos desde la página que quiero activar el "mensaje" y luego el sseServer.php en su lugar para obtener el mensaje a través de POST lo obtendrá de la tabla de base de datos.
¡Espero tener ayuda!