Webgriffe – Which queue manager to use?



Webgriffe – Which queue manager to use?

2 3


meet-magento-2014-presentation


On Github mmenozzi / meet-magento-2014-presentation

Webgriffe

Tailored Digital Works webgriffe.com | @webgriffe

  • 5+ Years of Experience with Magento
  • 5 Certified Developers (Zend & Magento)
  • 350+ Customers
  • 20+ Magento Extensions
  • 450+ Extensions Sold

Simple Scenario

Magento è composto da diverse componenti logiche: frontend, backend, database, media, cache e search engine. Nelle situazioni più semplici questi ruoli vengono svolti solitamente dalla stessa macchina server (fisica o virtuale che sia).

Complex Scenario

Al crescere delle esigenze però, può essere necessario separare questi ruoli su diverse macchine. Questo è possibile perché l'architettura software di Magento è stata progettata per rendere separabili anche fisicamente queste componenti logiche. Quindi possiamo andare verso architetture molto più complesse, dove ogni ruolo è svolto da una o più macchine separate. Ad esempio possiamo costruire una infrastruttura come quella mostrata dove dietro un load balancer / reverse proxy sono presenti 2 frontend server e dove abbiamo 1 cluster MySQL per il database, 1 caching server, 1 backend server, 1 search engine server ed un media storage (magari in CDN). Tutto questo utilizzando le funzionalità native di Magento.

Can we go over this limit?

Yes!

Queueing several syncrounous tasks allows us to go further.

A volte però, anche queste suddivisioni potrebbero non bastare, e sarebbe necessario poter identificare nuove componenti, particolrmente importanti per le nostre logiche di business, e renderle separabili dal resto dell'infrastruttura. Ma possiamo farlo? Possiamo andare oltre il limite progettuale di Magento? La risposta è si e uno dei modi con cui possiamo raggiungere questo obiettivo è sicuramente quello di introdurre un motore di code nella nostra infrastruttura. Infatti, mettendo in coda una operazione, o un gruppo di operazioni, che nativamente sono "sincrone" possiamo andare oltre questo limite.

Separation of components with Queue Manager & Workers

  • Queue Manager: collects & assigns tasks to workers
  • Workers: execute tasks (with repeat-until logic)
Il concetto è molto semplice. Si prende la nostra attuale infrasttura Magento, qualunque sia la sua complessità, e la si collega ad un motore di code che recepisce le operazioni che gli vengono comunicate e le assegna ad uno o più worker che eseguiranno le operazioni. I vari worker possono essere separati anche fisicamente su macchine diverse. Inoltre, generalmente, i motori di code hanno logiche abbastanza sofisticate e configurabili sull'assegnazione dei task ai worker collegati. Quindi, il primo aspetto che occorre notare è che attraverso l'introduzione di un motore di code abbiamo la libertà di identificare e separare nuove componenti logiche della nostra applicazione e, volendo, separarli anche fisicamente dal resto riducendo in questo modo il carico sulle altre macchine.

Response time improvments

Save 30% of the time by queuing index update on product save

Magento CE v. 1.8.1.0 with sample data. Index update on save enabled. Data collected with Xhprof profiler.

Inoltre, il secondo aspetto importante sull'utilizzo di un motore di code, è la possibiltà di ottenere significativi miglioramenti sui tempi di risposta. Ad esempio questi 2 schemi mostrano il salvataggio di un prodotto dal pannello admin fatta sulla stessa installazione Magento in 2 situazioni differenti. In entrambi i casi parliamo di Magento alla versione 1.8.1.0 con i sample data caricati e l'index update on save attivo. Lo schema di sinistra mostra l'operazione su Magento nativo al 100% in cui l'utente clicca sul bottone "Salva", Magento (lato server) comincia ad eseguire le operazioni necessarie ed infine la risposta viene restituita all'utente (che nel frattempo stava "perdendo tempo" aspettando una risposta). Le nostre misurazioni (fatte con Xhprof) ci dicono che tra le operazioni che vengono eseguite lato server, il 30% del tempo viene impiegato dall'operazione di aggiornamento dell'indice per quel prodotto; operazione che non è strettamente necessario sia sincrona. Lo schema di destra invece mostra la stessa operazione su una installazione in cui è stato introdotto un motore di code e una opportuna modifica applicativa. Come prima, l'utente clicca sul bottone "Salva", Magento (lato server) esegue le stesse operazioni di prima ma, quando arriva all'index update, questo task viene passato al motore di code che restituisce immediatamente una risposta a Magento che a sua volta restituisce immediatamente la risposta all'utente. Il tempo di passaggio del compito al motore di code è trascurabile quindi questa situazione permette all'utente di risparmiare il 30% del tempo ogni volta che salva un prodotto. L'operazione di aggiornamento dell'indice per il prodotto salvato verrà poi eseguita da un worker dedicato che, volendo, può anche lavorare su una macchina separata. Vediamo più nel dettaglio come possiamo ottenere questo risultato.

Online Hackathon Worldwide Experience

www.mage-hackathon.de/passed/online-hackathon-worldwide-31st-jan-1st-feb.html
  • 4 continents
  • 10+ countries
  • 100+ people

80+ open source Magento projects

github.com/magento-hackathon Questo tipo di ottimizzazioni è stato uno degli argomenti dell'ultimo Online Hackathon Worldwide che si è tenuto lo scorso 31 gennaio e a cui abbiamo partecipato. E' un evento molto interessante che, in quest'ultima edizione, ha raccolto più di 100 persone da più di 10 paesi sparsi in 4 continenti. Durante i Magento Hackathon passati sono stati sviluppati e rilasciati più di 80 progetti open source alcuni dei quali estremamente utili e famosi (tra cui Magento Composer Installer e Magento PSR0 Autoloader).

Online Hackathon Worldwide Experience

In sostanza ciò che si proponeva era di utilizzare l'estensione Lilmuckers_Queue per l'integrazione di un motore di code come Beanstalk e di aggiungere logica ad hoc per rendere asincrone alcune operazioni che nativamente non lo sono. Ovviamente prima di procedere è doveroso fare una breve analisi per capire se ciò che si vuole fare abbia senso e sia corretto. Infatti, non tutte le operazioni possono essere rese asincrone in quanto, ad esempio, sono necessarie per generare la risposta; oppure altre operazioni possono non essere così pesanti da giustificare la lavorazione.

Which queue manager to use?

The ones we can easly integrate with Magento are:

Attraverso Lilmuckers_Queue è possibile utilizzare qualunque motore, basta implementare il relativo adapter. Già inclusi nel modulo sono presenti 3 adapter per i motori: Beanstalk, Amazon SQS e Gearman.

Lilmuckers_Queue Extension

Defining a Queue Backend (for example Beanstalk)

<?xml version="1.0"?>
<!-- app/etc/local.xml -->
<config>
    <global>
		...
        <queue>
            <backend>beanstalkd</backend>
            <beanstalkd>
                <servers>
                    <server>
                        <host>127.0.0.1</host>
                    </server>
                </servers>
            </beanstalkd>
        </queue>
		...
    </global>
</config>

Lilmuckers_Queue Extension

Defining a Queue

<?xml version="1.0"?>
<!-- app/code/local/MyVendor/MyModule/etc/config.xml -->
<config>
...
    <queues>
        <queueName>
            <label>The Queue Name</label>
            <class>module/queueHandler</class>
            <workers>
                <taskName>
                    <class>module/worker</class>
                    <method>methodName</method>
                </taskName>
            </workers>
        </queueName>
    </queues>
...
</config>

Lilmuckers_Queue Extension

Adding tasks to Queue

$_queue = Mage::helper('lilqueue')->getQueue('queueName');
$_task = Mage::helper('lilqueue')->createTask(
	'taskName',
	array('data'=>'to provide', 'to'=>'the worker'),
	$storeToRunAs
);

// Optionally
$_task->setPriority(100)
    ->setDelay(60)
    ->setTtr(60);

$_queue->addTask($_task);

Lilmuckers_Queue Extension

Worker

class My_Module_Model_Worker extends Lilmuckers_Queue_Model_Worker_Abstract
{
    public function methodName(Lilmuckers_Queue_Model_Queue_Task $task)
    {
        //get the store assigned with the task
        $store = $task->getStore();
        //get the queue handler for this queue
        $queue = $task->getQueue();
        //get the data assigned with the task
        $data = $task->getData();
        //This task ended properly
        $task->success();
        //this task needs to be repeated
        $task->retry();
        //this task errored and we should drop it from the queue
        $task->hold();
        //this worker is taking a long time, we should extend it
        $task->touch();
    }
}
$ php /path/to/magento/shell/queue.php --watch <queues>

Queueing Magento Index Update

Open source code on GitHub github.com/webgriffe/index-queue-extension

Dall'esperienza dell'hackathon è quindi nata una estensione open source (disponibile su GitHub) che delega tutte le operazioni di aggiornamento dell'indice su una singola entità ad un motore di code e permette di ottenere quel miglioramento sui tempi di risposta che abbiamo visto prima. E' una estensione open source e utilizzablie gratuitamente. E' provvista di test automatici e di uno script di continous integration. Sulla pagina GitHub del repository è possibile trovare la documentazione sul suo utilizzo e le istruzioni per contribuire. Vediamola un po' più nel dettaglio.

Webgriffe_IndexQueue Extension

Architecture

L'estensione riscrive l'Indexer model nativo di Magento che viene richiamato ogni volta che sia necessario aggiornare l'indice per una singola entità (metodo processEntityAction). In questo modo, ogni volta che si cerca di aggiornare l'indice per una singola entità l'estensione può delegare l'operazione a Beanstalk (o altro queue manager) attraverso il relativo Adapter e restituire il controllo all'applicazione in tempo quasi 0. Contemporaneamente e su un processo separato sarà in ascolto l'IndexWorker che, sempre tramite l'Adapter, riceverà il task e lo eseguirà andando a richiamare l'Indexer model nativo di Magento; ottenendo così l'aggiornamento dell'indice in modo totalemente asincrono.

The same can be done with other components or tasks

Queues are for…

  • Entity index update
  • Cache cleaning
  • Stock update
  • Rendering email templates
  • Any task which the user doesn't need to wait for
Lo stesso tipo di implementazione può essere applicata anche ad altre componenti o operazioni che possono essere rese asincrone. Ad esempio è possibile implementare qualcosa di simile per la pulizia della cache, sopratutto nel backend. Infatti, dalle nostre misurazioni, abbiamo notato che, durante il salvataggio di un prodotto in admin, circa il 10% del tempo viene utilizzato per pulire le relative cache per il frontend. Come per l'aggiornamento dell'indice questa operazione non serve all'utente amministratore e si potrebbe quindi rendere asincrona delegandola al motore di code. Lo stesso discorso vale anche per l'aggiornamento dello stock durante il salvataggio del prodotto in admin. Benché, solitamente, l'invio effettivo dei messaggi e-mail venga già reso asincrono dalla maggior parte degli MTA di sistema, anche il rendering e l'invio dei messaggi e-mail (o altri tipi di messaggio come SMS) sono operazioni che, in alcune situazioni possono essere rese asincrone. Soprattutto l'operazione di rendering dei messaggi, a volte, non è così banale e vale quindi la pena delegarla al sistema di code insieme all'invio stesso. Insomma, le possibili applicazioni di questo tipo di ottimizzazione sono diverse e in generale possiamo dire che vale la pena prenderla in considerazione per qualsiasi operazione per cui l'utente non è necessario che aspetti.

Any Question?

Webgriffe

Tailored Digital Works webgriffe.com | @webgriffe

  • 5+ Years of Experience with Magento
  • 5 Certified Developers (Zend & Magento)
  • 350+ Customers
  • 20+ Magento Extensions
  • 450+ Extensions Sold

Thank you!