Cómo implementar eventos de escucha en PHP

aquí está mi problema: tengo un script (llamémosle comet.php) que es requerido por un script de cliente AJAX y esperamos que ocurra un cambio como este:

while(no_changes){ usleep(100000); //check for changes } 

No me gusta demasiado, no es muy escalable y es (mal) “mala práctica” Me gustaría mejorar este comportamiento con un semáforo (?) O, de todos modos, con una técnica de progtwigción concurrente. ¿Puedes darme algunos consejos sobre cómo manejar esto? (Lo sé, no es una respuesta corta, pero un punto de partida sería suficiente).

Editar : ¿qué pasa con LibEvent ?

Puede resolver este problema usando ZeroMQ .

ZeroMQ es una biblioteca que proporciona conectores supercargados para conectar cosas (hilos, procesos e incluso máquinas separadas) juntas.

Supongo que está tratando de enviar datos del servidor al cliente. Bueno, una buena forma de hacerlo es usar la API de EventSource ( rellenos disponibles ).

client.js

Se conecta a stream.php a través de EventSource.

 var stream = new EventSource('stream.php'); stream.addEventListener('debug', function (event) { var data = JSON.parse(event.data); console.log([event.type, data]); }); stream.addEventListener('message', function (event) { var data = JSON.parse(event.data); console.log([event.type, data]); }); 

router.php

Este es un proceso de larga duración que escucha los mensajes entrantes y los envía a cualquiera que los escuche.

 getSocket(ZMQ::SOCKET_PULL); $pull->bind("tcp://*:5555"); $pub = $context->getSocket(ZMQ::SOCKET_PUB); $pub->bind("tcp://*:5556"); while (true) { $msg = $pull->recv(); echo "publishing received message $msg\n"; $pub->send($msg); } 

stream.php

Cada usuario que se conecta al sitio obtiene su propio stream.php. Este script es de larga ejecución y espera mensajes del enrutador. Una vez que recibe un nuevo mensaje, este mensaje se enviará en formato EventSource.

 getSocket(ZMQ::SOCKET_SUB); $sock->setSockOpt(ZMQ::SOCKOPT_SUBSCRIBE, ""); $sock->connect("tcp://127.0.0.1:5556"); set_time_limit(0); ini_set('memory_limit', '512M'); header("Content-Type: text/event-stream"); header("Cache-Control: no-cache"); while (true) { $msg = $sock->recv(); $event = json_decode($msg, true); if (isset($event['type'])) { echo "event: {$event['type']}\n"; } $data = json_encode($event['data']); echo "data: $data\n\n"; ob_flush(); flush(); } 

Para enviar mensajes a todos los usuarios, simplemente envíelos al enrutador. El enrutador luego distribuirá ese mensaje a todas las transmisiones de escucha. Aquí hay un ejemplo:

 getSocket(ZMQ::SOCKET_PUSH); $sock->connect("tcp://127.0.0.1:5555"); $msg = json_encode(array('type' => 'debug', 'data' => array('foo', 'bar', 'baz'))); $sock->send($msg); $msg = json_encode(array('data' => array('foo', 'bar', 'baz'))); $sock->send($msg); 

Esto debería demostrar que no necesita node.js para hacer progtwigción en tiempo real. PHP puede manejarlo bien.

Aparte de eso, socket.io es una muy buena manera de hacer esto. Y puede conectarse a socket.io a su código PHP a través de ZeroMQ fácilmente.

Ver también

  • ZeroMQ
  • Enlaces de ZeroMQ PHP
  • ZeroMQ es la respuesta – Ian Barber (Video)
  • socket.io

Realmente depende de lo que estés haciendo en tu script del lado del servidor. Hay algunas situaciones en las que no tienes más opción que hacer lo que estás haciendo arriba.

Sin embargo, si estás haciendo algo que implica una llamada a una función que bloqueará hasta que algo suceda, puedes utilizar esto para evitar carreras en lugar de la llamada usleep() (que en mi humilde opinión es la parte que se consideraría “mala práctica”) .

Digamos que estaba esperando datos de un archivo u otro tipo de flujo que bloquee. Podrías hacer esto:

 while (($str = fgets($fp)) === FALSE) continue; // Handle the event here 

Realmente, PHP es el lenguaje equivocado para hacer cosas como esta. Pero hay situaciones (lo sé porque las he tratado yo mismo) donde PHP es la única opción.

Por mucho que me guste PHP, debo decir que PHP no es la mejor opción para esta tarea. Node.js es mucho, mucho mejor para este tipo de cosas y escala muy bien. También es muy fácil de implementar si tienes conocimiento de JS.

Ahora, si no quiere perder ciclos de CPU, tiene que crear un script PHP que se conectará a un servidor de algún tipo en un puerto determinado. El servidor especificado debe escuchar las conexiones en el puerto elegido y cada X cantidad de tiempo verificar lo que quiera verificar (entradas db para nuevas publicaciones, por ejemplo) y luego envía el mensaje a cada cliente conectado que la nueva entrada está lista.

Ahora, no es tan difícil implementar esta architecture de cola de eventos en PHP, pero tardaría literalmente 5 minutos en hacerlo con Node.js y Socket.IO, sin preocuparse de si funcionaría en la mayoría de los navegadores.

Estoy de acuerdo con el consenso aquí de que PHP no es la mejor solución aquí. Realmente necesita buscar tecnologías dedicadas en tiempo real para la solución a este problema asíncrono de entregar datos de su servidor a sus clientes. Parece que está intentando implementar HTTP-Long Polling, que no es una tarea fácil de resolver entre navegadores. Los desarrolladores de los productos Comet lo han abordado en numerosas ocasiones, por lo que sugiero que consulten una solución Comet o, incluso mejor, una solución WebSocket con soporte alternativo para navegadores antiguos.

Sugiero que deje que PHP haga la funcionalidad de la aplicación web que es buena y elija una solución dedicada para su funcionalidad asincrónica en tiempo real.

Necesitas una biblioteca en tiempo real.

Un ejemplo es Ratchet http://socketo.me/

La parte que se ocupa del pub sub se trata en http://socketo.me/docs/wamp

La limitación aquí es que PHP también debe ser el que inicie los datos mutables.

En otras palabras, esto mágicamente no te permitirá suscribirte cuando se actualice MySQL. Pero si puede editar el código de configuración de MySQL, puede agregar la parte de publicación allí.