13

Desidero scrivere un worker per beanstalkd in php, utilizzando un controller Zend Framework 2. Si avvia tramite la CLI e verrà eseguito per sempre, richiedendo lavori da beanstalkd come this example.Considerazioni sulla memoria per script php con esecuzione prolungata

In semplice codice pseudo-like:

while (true) { 
    $data = $beanstalk->reserve(); 

    $class = $data->class; 
    $params = $data->params; 

    $job = new $class($params); 
    $job(); 
} 

Il $job ha qui un metodo naturalmente __invoke(). Tuttavia, alcune cose in questi lavori potrebbero essere in esecuzione per molto tempo. Alcuni potrebbero funzionare con una notevole quantità di memoria. Alcuni potrebbero aver iniettato l'oggetto $beanstalk, per avviare nuovi lavori o avere un'istanza Zend\Di\Locator per estrarre oggetti dal DIC.

Sono preoccupato per questa configurazione per gli ambienti di produzione a lungo termine, poiché potrebbero verificarsi riferimenti circolari e (al momento) non faccio "esplicitamente" alcuna raccolta di dati inutili mentre questa azione potrebbe essere eseguita per settimane/mesi/anni *.

*) In beanstalk, reserve è una chiamata di blocco e se non è disponibile alcun lavoro, questo operatore attenderà fino a quando non riceve risposta da beanstalk.

La mia domanda: in che modo gestirò questo php a lungo termine e dovrei prendere ogni precauzione speciale per impedire che questo blocchi?

questo ho considerato e potrebbe essere utile (ma per favore correggetemi se sbaglio e aggiungere di più se possibile):

  1. Usa gc_enable() prima di iniziare il ciclo
  2. Usa gc_collect_cycles() in ogni iterazione
  3. unset $job in ogni iterazione
  4. riferimenti esplicitamente unset in __destruct() da un $job

(NB: Aggiornamento da qui)

ho eseguito alcuni test con posti di lavoro arbitrarie. I lavori che ho incluso erano: "semplice", basta impostare un valore; "longarray", crea una serie di 1.000 valori; "producer", lascia che il loop inietti $pheanstalk e aggiunga tre semplici alla coda (quindi ora c'è un riferimento da job a beanstalk); "locatoraware", dove viene dato un Zend\Di\Locator e tutti i tipi di lavoro sono istanziati (anche se non invocati). Ho aggiunto 10.000 lavori alla coda, quindi ho riservato tutti i lavori in coda.

Risultati per (il consumo di memoria per 1000 posti di lavoro, con memory_get_usage()) "simplejob"

0:  56392 
1000: 548832 
2000: 1074464 
3000: 1538656 
4000: 2125728 
5000: 2598112 
6000: 3054112 
7000: 3510112 
8000: 4228256 
9000: 4717024 
10000: 5173024 

Scegliere un posto di lavoro a caso, misurando lo stesso come sopra.Distribuzione:

["Producer"] => int(2431) 
["LongArray"] => int(2588) 
["LocatorAware"] => int(2526) 
["Simple"] => int(2456) 

memoria:

0:  66164 
1000: 810056 
2000: 1569452 
3000: 2258036 
4000: 3083032 
5000: 3791256 
6000: 4480028 
7000: 5163884 
8000: 6107812 
9000: 6824320 
10000: 7518020 

l'esecuzione del codice da sopra è aggiornata a questo:

$baseMemory = memory_get_usage(); 
gc_enable(); 

for ($i = 0; $i <= 10000; $i++) { 
    $data = $bheanstalk->reserve(); 

    $class = $data->class; 
    $params = $data->params; 

    $job = new $class($params); 
    $job(); 

    $job = null; 
    unset($job); 

    if ($i % 1000 === 0) { 
     gc_collect_cycles(); 
     echo sprintf('%8d: ', $i), memory_get_usage() - $baseMemory, "<br>"; 
    } 
} 

Come è nota, il consumo di memoria è in php non leva e ridotto al minimo, ma aumenta nel tempo.

+0

Questa è una domanda interessante, ho aggiunto alcune ricerche correlate sull'uso di 'gc_collect_cycles' http://stackoverflow.com/questions/38850391/when-does-php-run-garbage-collection-in-long-running-scripts/ 38850392 # 38850392 – mcfedr

risposta

2

ho finito di benchmarking mia linea attuale base di codice per la linea, dopo di che sono venuto a questo:

$job = $this->getLocator()->get($data->name, $params); 

utilizza l'iniezione di dipendenza Zend\Di quale istanza responsabile traccia delle istanze attraverso l'intero processo. Quindi, dopo che un lavoro è stato richiamato e può essere rimosso, il gestore dell'istanza lo ha ancora tenuto in memoria. Se non si utilizza Zend\Di per creare un'istanza dei lavori, si ottiene immediatamente un consumo di memoria statico invece di uno lineare.

+0

Anche io sto affrontando un problema simile. Pensi che i metodi non ti aiutino? -gc_enable() prima di iniziare le ciclo gc_collect_cycles -Utilizzare() in ogni iterazione -Unset $ lavoro in ogni iterazione riferimenti -Explicitly unset in __destruct() da un $ job –

+0

Basta assicurarsi di non tenere un caso di la classe dentro il contenitore. Ho finito per utilizzare ServiceManager e impostare il comportamento condiviso su false. –

1

Per la sicurezza della memoria, non utilizzare il ciclo dopo ogni sequenza di lavoro in PHP. Ma basta creare semplice script bash per fare looping:

while [ true ] ; do 
    php do_jobs.php 
done 

Hey there, con do_jobs.php contiene qualcosa come:

// ... 

$data = $beanstalk->reserve(); 

$class = $data->class; 
$params = $data->params; 

$job = new $class($params); 
$job(); 


// ... 

semplice diritto? ;)

+1

Vorrei mantenere il controllo all'interno di php. Quando qualcosa non funziona durante l'esecuzione del lavoro, bash non ne è al corrente e avvia il lavoro successivo. Ne hai un controllo minore in questa situazione. Inoltre, con un'app cli ZF2, invochi un controller direttamente attraverso (ad esempio) 'riserva di lavoro app.php - watch default - sleep - tra 100 --log./Data/log/worker', che è ciò che io mi piacerebbe fare –

+0

Le sequenze di lavoro dipendono da un altro lavoro (in loop)? se lo fa, allora devi usare la soluzione PHP completa. Se ogni indipendenza di lavoro, combinazione IMHO bash e php è la soluzione migliore per evitare perdite di memoria PHP. – Superbiji

1

Ho in genere riavviato regolarmente lo script, anche se non è necessario farlo dopo l'esecuzione di ogni lavoro (a meno che non lo si voglia, ed è utile per cancellare la memoria). Ad esempio, è possibile eseguire fino a 100 lavori o più alla volta o fino a quando lo script ha usato dire 20 MB di RAM e quindi uscire dallo script per essere immediatamente riprodotti.

Il mio blogpost allo http://www.phpscaling.com/2009/06/23/doing-the-work-elsewhere-sidebar-running-the-worker/ ha alcuni script di shell di esempio per rieseguire gli script.

+1

Anche qui, le considerazioni sulla memoria sono trattate usando bash per controllare la sequenza al posto di php stesso. Speravo in una soluzione solo php, ma a quanto pare potrebbe non essere possibile. La strategia del codice di uscita sembra tuttavia dare più controllo sul flusso. –