2011-09-19 14 views
5

qui è il mio problema: Ho uno script (chiamiamolo comet.php) whic è requsted da uno script client AJAX e attendere per un cambiamento per accadere in questo modo:Come implementare evento ascoltare in PHP

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

Non mi piace troppo, non è molto scalabile ed è (imho) "cattiva pratica" Vorrei migliorare questo comportamento con una semaforo (?) O comunque con la programmazione simultanea della tecnica . Potete darmi qualche consiglio su come gestirlo? (Lo so, non è una risposta breve, ma sarebbe sufficiente un punto di partenza.)

Modifica: che dire di LibEvent?

+0

Cosa stai cercando di ottenere? Il metodo usuale consiste nel richiedere al client di chiamare periodicamente lo script per verificare le modifiche. C'è qualche ragione per cui vuoi farlo lato server? – JJJ

+3

PHP non è proprio un linguaggio da usare per COMET. Usa il nodo.js o qualcos'altro che funziona in modo asincrono (ad esempio ciclone tornado o greenlet). Utilizzando PHP in esecuzione su un server web basato su thread/processo, si ha un enorme sovraccarico. – ThiefMaster

+0

@Juhana, il motivo è quello di evitare la verifica periodica del cambiamento e di avere una soluzione 'reverse-ajax'. @thiefMaster so che ci sono alcune soluzioni COMET server là fuori, ma penso davvero che un back-end di PHP possa essere possibile, e finché scriverò il mio login business in PHP sarebbe molto meglio non riscrivere in un'altra lingua evitando il codice duplicazione. Puoi spiegarmi perché un backend comet di PHP sarebbe quello in testa? – ArtoAle

risposta

12

È possibile risolvere questo problema utilizzando ZeroMQ.

ZeroMQ è una libreria che fornisce socket sovralimentati per collegare le cose (thread, processi e persino macchine separate) insieme.

Suppongo che tu stia cercando di inviare dati dal server al client. Bene, un buon modo per farlo è usare lo EventSource API (polyfills available).

Client.js

Si collega al stream.php attraverso 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

Si tratta di un processo di lunga durata che ascolta i messaggi in arrivo e li invia a chiunque di ascolto.

<?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

Ogni utente che si collega al sito ottiene la sua propria stream.php. Questo script è di lunga durata e attende qualsiasi messaggio dal router. Una volta ricevuto un nuovo messaggio, questo messaggio verrà emesso nel 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(); 
} 

per inviare messaggi a tutti gli utenti, basta inviare al router. Il router distribuirà quindi quel messaggio a tutti i flussi di ascolto. Ecco un esempio:

Questo dovrebbe dimostrare che non è necessario che node.js esegua la programmazione in tempo reale. PHP può gestirlo bene.

Oltre a ciò, socket.io è un modo davvero piacevole per farlo. E potresti facilmente connetterti a socket.io al tuo codice PHP via ZeroMQ.

Vedi anche

+2

Nota: recentemente ho fatto alcuni benchmark. Ad un certo punto questo non si adatta molto bene. Ho provato a inviare poche migliaia di messaggi in un breve periodo di tempo con poche centinaia di client connessi. Se si utilizza Apache, rallenterà a causa della quantità di cambio di contesto tra i thread. A un certo punto, il nodo o il modulo nginx-push-stream saranno gli strumenti migliori per farlo. Tuttavia, è possibile con PHP e funziona. – igorw

+1

Semplicemente ami la facilità e la semplicità di questo, così come l'esempio molto descrittivo. 6 anni su tutta la linea e ancora una risposta semplice aggiornata. Grazie! –

4

Dipende molto da ciò che si sta facendo nello script lato server. Ci sono alcune situazioni in cui non hai altra scelta che fare ciò che stai facendo sopra.

Tuttavia, se si sta facendo qualcosa che comporta una chiamata a una funzione che bloccherà fino a quando qualcosa accade, è possibile utilizzare questo per evitare corse invece della chiamata usleep() (che è IMHO la parte che sarebbe considerata "cattiva pratica" ").

Supponiamo che stiate aspettando i dati da un file o da un altro tipo di flusso che blocca. Si potrebbe fare questo:

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

In realtà, PHP è la lingua sbagliata per fare cose come questa. Ma ci sono situazioni (lo so perché le ho affrontate io stesso) dove PHP è l'unica opzione.

+1

, in realtà, vorrei ridurre il sovraccarico della CPU rimuovendo il noop mentre (ecco perché c'è la chiamata usleep()) – ArtoAle

+2

@ArtoAle È possibile ' Aspetta un evento senza alcun tipo di loop in PHP - non ha gestori di eventi come ad es javascript. Ma facendo come descritto sopra, stai lasciando che la chiamata a 'fgets()' esegua l'attesa, piuttosto che check-sleep-check-sleep-check ... Nel tuo esempio, se l'evento si verifica durante un 'usleep() 'dovrai aspettare la fine del sonno prima che tu possa gestirlo. Usando il controllo (di blocco) come condizione del ciclo, 'fgets()' ritorna immediatamente non appena si verifica l'evento e puoi gestirlo immediatamente. – DaveRandom

+0

ok, hai ragione. Il punto è che con [System V IPC] (http://www.php.net/manual/en/intro.sem.php) posso usare il blocking call per il semaphore aquiring (che risulta nella stessa soluzione che hai proposto) Il problema è che non so davvero come usare il semaforo per avere un comportamento applicativo "simile all'evento" – ArtoAle

2

Per quanto mi piaccia PHP, devo dire che PHP non è la scelta migliore per questo compito. Node.js è molto, molto meglio per questo genere di cose e si adatta davvero bene. È anche molto semplice da implementare se si dispone della conoscenza di JS.

Ora, se non si desidera sprecare cicli di CPU, è necessario creare uno script PHP che si connetterà a un server di qualche tipo su una determinata porta. Il server specificato deve ascoltare le connessioni sulla porta prescelta e ogni X quantità di tempo controlla per tutto ciò che si desidera controllare (voci db per nuovi messaggi, ad esempio) e poi invia il messaggio a tutti i client connessi che la nuova voce è pronta.

Ora, non è così difficile implementare questa architettura di coda di eventi in PHP, ma ci vorranno letteralmente 5 minuti per farlo con Node.js e Socket.IO, senza preoccuparsi che funzioni in gran parte i browser.

0

Concordo sul fatto che PHP non è la soluzione migliore qui. Devi davvero guardare allo realtime technologies dedicato per la soluzione a questo problema asincrono di distribuzione dei dati dal tuo server ai tuoi clienti. Sembra che tu stia cercando di implementare il Polling HTTP-Long che non è una cosa facile da risolvere nel cross-browser. È stato affrontato numerose volte dagli sviluppatori di prodotti Comet, quindi ti suggerisco di dare un'occhiata a una soluzione Comet, o ancora meglio una soluzione WebSocket con supporto fallback per i browser più vecchi.

Suggerirei di consentire a PHP di eseguire le funzionalità dell'applicazione Web in modo ottimale e di scegliere una soluzione dedicata per la funzionalità in tempo reale, con eventi e asincroni.

0

Hai bisogno di una libreria in tempo reale.

Un esempio è cricchetto http://socketo.me/

La parte che si occupa della sub pub è discussa http://socketo.me/docs/wamp

La limitazione è che PHP deve anche essere quello di avviare i dati mutabili.

In altre parole questo non consente magicamente di sottoscrivere quando MySQL viene aggiornato. Ma se è possibile modificare il codice di impostazione di MySQL, è possibile aggiungere la parte di pubblicazione lì.