snipt

Ctrl+h for KB shortcuts

Markdown

web2py and Redis Queue

RQ (Redis Queue)

RQ (Redis Queue) is a simple Python library for queueing jobs and processing them in the background with workers. It is backed by Redis and it is designed to have a low barrier to entry. It should be integrated in your web(2py) stack easily.

http://python-rq.org

web2py

Free open source full-stack framework for rapid development of fast, scalable, secure and portable database-driven web-based applications. Written and programmable in Python.

http://www.web2py.com

Queueing jobs with RQ and web2py

web2py as many other web frameworks works in a request -> response environment, which means that there is a lifetime for things to be done. This lifetime we call "request time", it is the time between the client requests a resource (i.e hits an url of our app or post a form) and the time that the server gives the response back to the client (i.e: The server sends html, json or any other kind of response).

The problem with this is the fact that we have a time-out and the user does not want to wait for tasks to be done, I mean in example for creating image thumbnails, users have to upload a picture and then wait for the thumbnail to be created to have a response from server. Or in the case of sending an email, user fill a contact form and have to wait for the message to be sent. It can take a long time and sometimes it will fail.

The solution is to enqueue that jobs on background and then watch its results to give a response to the user, this response can be given through a websocket or ajax long pooling. (I will not cover this here)

Setting up

  • install Redis
    • In debian based linuxes you can do: sudo apt-get install redis-server
  • Install RQ (redis queue)
    • sudo pip install rq

case 1 : Sending email in background

User will fill our contact form and then click in submit, instead of sending the email we are going to enqueue the email to be sent by the redis queue.

1. In your models create your Queue object (also you need to have the mail settings)

models/db.py

from gluon.tools import Mail
mail = Mail()
mail.settings.server = "smtp.google.com:587"
mail.settings.sender = "you@gmail.com"
mail.settings.login = "you:yourpassword"

models/queue.py

from redis import Redis
from rq import Queue
q = Queue(connection=Redis())

The above will use the default Redis connection port to localhost, take a look at RQ docs if you need to set another redis server.

2. In your controller create the contact action which returns a form.

controllers/default.py

def contact():
    form = SQLFORM.factory(Field("name"), Field("message"))
    if form.accepts(request):
        # enqueue the email to be sent!
        q.enqueue(mail.send,
                  to="you@gmail.com",
                  subject="%(name)s contacted you" % form.vars,
                  message=form.vars.message)
        # do whatever you want
        response.flash = "email successfully sent!"

case 2 : Creating an image thumbnail

User will upload a picture and you are going to create a THUMBNAIL and store the thumbnail in /static/thumbs folder

1. Define some models

models/db.py

Picture = db.define_table("pictures",
    Field("name"),
    Field("picture", "upload")
)

models/queue.py

from redis import Redis
from rq import Queue
q = Queue(connection=Redis())

2. Create the form

controllers/default.py

# requires PIL to be installed
# sudo apt-get install python-imaging
from gluon.contrib.imageutils import THUMB

def add_picture():
    form = SQLFORM(Picture, submit_button="send")
    if form.process().accepted:
        #enqueue thumbnail to be created
        q.enqueue(THUMB, form.vars.picture)

Put the workers to work

On the cases above we just enqueued tasks to be executed by the workers, now we need the worker running.

web2py environment

The worker should run under the web2py environment, because we are using web2py modules to send emails and create the thumbnail, so the RQ worker should be started with this script.

1. Create the web2py RQ worker

/some/path/web2py-rq.py

import sys
from rq import Queue, Connection, Worker

# Provide queue names to listen to as arguments to this script,
# similar to rqworker
with Connection():
    qs = map(Queue, sys.argv[1:]) or [Queue()]
    w = Worker(qs)
    w.work()

Start the above worker under web2py environment

cd /path/to/web2py
python web2py.py -S yourappname -M -R /some/path/web2py-rq.py

With the above worker running the enqueued tasks will be executed and then worker will keep listening for new tasks.

You can also put the worker to run in backgroungm for this you shoud use nohup python web2py.py -S yourappname -M -R /some/path/web2py-rq.py & or even better you can put this to run under the supervidord

with the worker running you should see this console:

python web2py/web2py.py -S app -M -R /projects/web2py-rq.py 
web2py Web Framework
Created by Massimo Di Pierro, Copyright 2007-2012
Version 2.4.1-alpha.2+timestamp.2012.12.28.16.18.51
Database drivers available: SQLite(sqlite3), MySQL(pymysql), PostgreSQL(pg8000), IMAP(imaplib)
[2012-12-31 00:33] DEBUG: worker: Registering birth of worker precise64.15755
[2012-12-31 00:33] INFO: worker: RQ worker started, version 0.3.2
[2012-12-31 00:33] INFO: worker: 
[2012-12-31 00:33] INFO: worker: *** Listening on default...
[2012-12-31 00:34] INFO: worker: default: send(to='someone@gmail.com', message='blah', subject='testing') (a069b2c6-f908-4806-8534-b00c43996cf4)

Monitoring

RQ has some nice ways for monitoring the jobs by command-line or by its dashboard.

command line:

To see what queues exist and what workers are active, just type rqinfo:

$ rqinfo
high       |██████████████████████████ 20
low        |██████████████ 12
default    |█████████ 8
3 queues, 45 jobs total

Bricktop.19233 idle: low
Bricktop.19232 idle: high, default, low
Bricktop.18349 idle: default
3 workers, 3 queues

As you can see it is possible to start many workers.

Dashboard

The easiest way is probably to use the RQ dashboard, a separately distributed tool, which is a lightweight webbased monitor frontend for RQ, which looks like this:

RQ dashboard https://github.com/nvie/rq-dashboard

dash

https://snipt.net/embed/7204ab2b27804acb5bb59c68b153b7bb/
https://snipt.net/raw/7204ab2b27804acb5bb59c68b153b7bb/
7204ab2b27804acb5bb59c68b153b7bb
markdown
Markdown
177
2016-09-20T01:07:37
True
False
True
Dec 30, 2012 at 09:05 PM
/api/public/snipt/52562/
web2py-and-redis-queue
<h2>RQ (Redis Queue)</h2> <p>RQ (Redis Queue) is a simple Python library for queueing jobs and processing them in the background with workers. It is backed by Redis and it is designed to have a low barrier to entry. It should be integrated in your web(2py) stack easily.</p> <p><a href="http://python-rq.org">http://python-rq.org</a></p> <h2>web2py</h2> <p>Free open source full-stack framework for rapid development of fast, scalable, secure and portable database-driven web-based applications. Written and programmable in Python.</p> <p><a href="http://www.web2py.com">http://www.web2py.com</a></p> <h1>Queueing jobs with RQ and web2py</h1> <p>web2py as many other web frameworks works in a request -> response environment, which means that there is a lifetime for things to be done. This lifetime we call "request time", it is the time between the client <strong>request</strong>s a resource (i.e hits an url of our app or post a form) and the time that the server gives the <strong>response</strong> back to the client (i.e: The server sends html, json or any other kind of response).</p> <p>The problem with this is the fact that we have a time-out and the user does not want to wait for tasks to be done, I mean in example for creating image thumbnails, users have to upload a picture and then wait for the thumbnail to be created to have a response from server. Or in the case of sending an email, user fill a contact form and have to wait for the message to be sent. It can take a long time and sometimes it will fail.</p> <p>The solution is to enqueue that jobs on background and then watch its results to give a response to the user, this response can be given through a websocket or ajax long pooling. (I will not cover this here) </p> <h2>Setting up</h2> <ul> <li>install Redis <ul> <li>In debian based linuxes you can do: <code>sudo apt-get install redis-server</code></li> </ul></li> <li>Install RQ (redis queue) <ul> <li><code>sudo pip install rq</code></li> </ul></li> </ul> <h2>case 1 : Sending email in background</h2> <p>User will fill our contact form and then click in submit, instead of sending the email we are going to enqueue the email to be sent by the redis queue.</p> <h4>1. In your models create your Queue object (also you need to have the mail settings)</h4> <p><code>models/db.py</code></p> <pre><code>from gluon.tools import Mail mail = Mail() mail.settings.server = "smtp.google.com:587" mail.settings.sender = "you@gmail.com" mail.settings.login = "you:yourpassword" </code></pre> <p><code>models/queue.py</code></p> <pre><code>from redis import Redis from rq import Queue q = Queue(connection=Redis()) </code></pre> <p>The above will use the default Redis connection port to localhost, take a look at RQ docs if you need to set another redis server.</p> <h4>2. In your controller create the contact action which returns a form.</h4> <p><code>controllers/default.py</code></p> <pre><code>def contact(): form = SQLFORM.factory(Field("name"), Field("message")) if form.accepts(request): # enqueue the email to be sent! q.enqueue(mail.send, to="you@gmail.com", subject="%(name)s contacted you" % form.vars, message=form.vars.message) # do whatever you want response.flash = "email successfully sent!" </code></pre> <h2>case 2 : Creating an image thumbnail</h2> <p>User will upload a picture and you are going to create a THUMBNAIL and store the thumbnail in /static/thumbs folder</p> <h4>1. Define some models</h4> <p><code>models/db.py</code></p> <pre><code>Picture = db.define_table("pictures", Field("name"), Field("picture", "upload") ) </code></pre> <p><code>models/queue.py</code></p> <pre><code>from redis import Redis from rq import Queue q = Queue(connection=Redis()) </code></pre> <h4>2. Create the form</h4> <p><code>controllers/default.py</code></p> <pre><code># requires PIL to be installed # sudo apt-get install python-imaging from gluon.contrib.imageutils import THUMB def add_picture(): form = SQLFORM(Picture, submit_button="send") if form.process().accepted: #enqueue thumbnail to be created q.enqueue(THUMB, form.vars.picture) </code></pre> <h1>Put the workers to work</h1> <p>On the cases above we just enqueued tasks to be executed by the workers, now we need the worker running.</p> <h2>web2py environment</h2> <p>The worker should run under the web2py environment, because we are using web2py modules to send emails and create the thumbnail, so the RQ worker should be started with this script.</p> <h3>1. Create the web2py RQ worker</h3> <p><code>/some/path/web2py-rq.py</code></p> <pre><code>import sys from rq import Queue, Connection, Worker # Provide queue names to listen to as arguments to this script, # similar to rqworker with Connection(): qs = map(Queue, sys.argv[1:]) or [Queue()] w = Worker(qs) w.work() </code></pre> <h3>Start the above worker under web2py environment</h3> <pre><code>cd /path/to/web2py python web2py.py -S yourappname -M -R /some/path/web2py-rq.py </code></pre> <p>With the above worker running the enqueued tasks will be executed and then worker will keep listening for new tasks.</p> <p>You can also put the worker to run in backgroungm for this you shoud use <code>nohup python web2py.py -S yourappname -M -R /some/path/web2py-rq.py &amp;</code> or even better you can put this to run under the <strong>supervidord</strong></p> <h4>with the worker running you should see this console:</h4> <pre><code>python web2py/web2py.py -S app -M -R /projects/web2py-rq.py web2py Web Framework Created by Massimo Di Pierro, Copyright 2007-2012 Version 2.4.1-alpha.2+timestamp.2012.12.28.16.18.51 Database drivers available: SQLite(sqlite3), MySQL(pymysql), PostgreSQL(pg8000), IMAP(imaplib) [2012-12-31 00:33] DEBUG: worker: Registering birth of worker precise64.15755 [2012-12-31 00:33] INFO: worker: RQ worker started, version 0.3.2 [2012-12-31 00:33] INFO: worker: [2012-12-31 00:33] INFO: worker: *** Listening on default... [2012-12-31 00:34] INFO: worker: default: send(to='someone@gmail.com', message='blah', subject='testing') (a069b2c6-f908-4806-8534-b00c43996cf4) </code></pre> <h1>Monitoring</h1> <p>RQ has some nice ways for monitoring the jobs by command-line or by its dashboard.</p> <h2>command line:</h2> <p>To see what queues exist and what workers are active, just type rqinfo:</p> <pre><code>$ rqinfo high |██████████████████████████ 20 low |██████████████ 12 default |█████████ 8 3 queues, 45 jobs total Bricktop.19233 idle: low Bricktop.19232 idle: high, default, low Bricktop.18349 idle: default 3 workers, 3 queues </code></pre> <p>As you can see it is possible to start many workers.</p> <h2>Dashboard</h2> <p>The easiest way is probably to use the RQ dashboard, a separately distributed tool, which is a lightweight webbased monitor frontend for RQ, which looks like this:</p> <p>RQ dashboard <a href="https://github.com/nvie/rq-dashboard">https://github.com/nvie/rq-dashboard</a></p> <p><img src="http://python-rq.org/img/dashboard.png" alt="dash" /></p>
pyplanet, python, redis, snipt-expand, web2py
Copyrighted, illegal, or inappropriate content? Email support@snipt.net.