2011-09-19 54 views
5

aquí está mi problema: Tengo un script (vamos a llamarlo comet.php) whic se requsted por un script de cliente AJAX y esperar a que un cambio a suceder así:Cómo implementar evento escuchar en PHP

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

No me gusta demasiado, no es muy escalable y es (imho) "mala práctica" Me gustaría mejorar este comportamiento con un semáforo (?) O de todos modos la técnica de programació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é hay de LibEvent?

+0

¿Qué estás tratando de lograr? El método habitual es hacer que el cliente llame al script periódicamente para verificar los cambios. ¿Hay alguna razón por la que quieres hacerlo desde el servidor? – JJJ

+3

PHP no es realmente un lenguaje que debe usar para COMET. Use el nodojs o algo más que funciona de forma asíncrona (python tornado o greenlets, por ejemplo). Al utilizar PHP ejecutándose en un servidor web basado en hilos/proceso, tiene una gran sobrecarga. – ThiefMaster

+0

@Juhana, la razón es 'evitar 'la comprobación periódica de cambio y tener una solución' reverse-ajax'. @thiefMaster Sé que hay alguna solución de servidor COMET, pero realmente creo que un back-end php puede ser posible, y mientras escriba mi inicio de sesión empresarial en PHP, sería mejor no volver a escribir en otro idioma evitando el código duplicación. ¿Puedes explicarme por qué un backend de cometa PHP sería ese overheading? – ArtoAle

Respuesta

12

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á intentando enviar datos desde el servidor al cliente. Bueno, una buena forma de hacerlo es usando el EventSource API (polyfills available).

Client.js

conecta a través de stream.php 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 escuche.

<?php 

$context = new ZMQContext(); 

$pull = $context->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 propia 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.

<?php 

$context = new ZMQContext(); 

$sock = $context->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:

<?php 

$context = new ZMQContext(); 

$sock = $context->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 programación en tiempo real. PHP puede manejarlo bien.

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

Ver también

+2

Tenga en cuenta: Recientemente hice algunos puntos de referencia. En algún momento, esto no se escala muy bien. Intenté enviar unos mil mensajes en un corto período de tiempo con unos pocos cientos de clientes conectados. Si está utilizando Apache, se ralentizará debido a la cantidad de cambio de contexto entre los hilos. En algún punto, el nodo o el módulo nginx-push-stream serán las mejores herramientas para hacer esto. Sin embargo, es posible con PHP y funciona. – igorw

+1

Simplemente amo la facilidad y simplicidad de esto así como el ejemplo muy descriptivo. 6 años después y todavía una respuesta simple actualizada. ¡Gracias! –

4

Realmente depende de lo que está haciendo en el 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 usted está haciendo algo que implica una llamada a una función que bloqueará hasta que suceda algo, puede utilizar esto para evitar las carreras en lugar de la llamada usleep() (que es mi humilde opinión, la parte que pueda ser considerada "mala práctica ").

Digamos que estaba esperando datos de un archivo u otro tipo de flujo que bloquea. Usted puede 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.

+1

, me gustaría reducir la sobrecarga de la CPU quitando el noop mientras (por eso está la llamada usleep) – ArtoAle

+2

@Artole Puede ' t esperar un evento sin algún tipo de bucle en PHP - no tiene controladores de eventos como por ejemplo javascript Pero al hacerlo como se describe anteriormente, está permitiendo que la llamada 'fgets()' haga la espera, en lugar de verificar-verificar-dormir-verificar-en-espera ... En su ejemplo, si el evento ocurre durante un 'usleep() 'tendrás que esperar hasta el final del sueño para poder manejarlo. Al usar la comprobación (de bloqueo) como la condición de bucle, 'fgets()' regresa inmediatamente tan pronto como el evento ha sucedido y puede manejarlo de inmediato. – DaveRandom

+0

bien, tienes razón. El punto es que con [System V IPC] (http://www.php.net/manual/en/intro.sem.php) puedo usar la llamada de bloqueo a la adquisición de semáforo (que resulta en la misma solución que usted propuso) El problema es que realmente no sé cómo usar semáforos para tener un comportamiento de aplicación "similar a un evento". – ArtoAle

2

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 arquitectura de cola de eventos en PHP, pero le tomaría literalmente 5 minutos hacerlo con Node.js y Socket.IO, sin preocuparse de si funcionará en la mayoría de navegadores.

0

Estoy de acuerdo con el consenso aquí que PHP no es la mejor solución a este problema. Realmente necesita consultar el realtime technologies dedicado 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.

me gustaría sugerir que permita que hacer con PHP la funcionalidad de la aplicación web que es bueno en y elija una solución dedicada para su tiempo real, funcionalidad evented, asíncrono.

0

Es necesario una biblioteca en tiempo real.

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

La parte que se encarga de la sub pub es discutido en http://socketo.me/docs/wamp

La limitación aquí es que PHP también tiene que 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í.

Cuestiones relacionadas