Nicolas314

All my geeky stuff ends up here. Mostly Unix-related

Posts Tagged ‘web.py

gunicorn basics

leave a comment »

Following up on a previous post about fapws3 and its inability to spread the load on several workers, here is a quick rundown of how to get a gunicorn instance running in a matter of minutes.

If you do not want to disturb your system’s Python installation, virtualenv is the perfect tool. It will re-create the equivalent of a chrooted environment for Python stuff only, allowing you to mess up as much as you want with libraries without having to trash system libraries.

$ mkdir webtests
$ cd webtests
$ virtualenv app
New python executable in app/bin/python
Installing distribute..................................................................................................................................................................................done.
$ . app/bin/activate
$ easy_install -U gunicorn
Searching for gunicorn
Reading http://pypi.python.org/simple/gunicorn/
Reading http://github.com/benoitc/gunicorn
Reading http://www.gunicorn.org
Reading http://gunicorn.org
Best match: gunicorn 0.12.1
Downloading http://pypi.python.org/packages/source/g/gunicorn/gunicorn-0.12.1.tar.gz#md5=6540ec02de8e00b6b60c28a26a019662
Processing gunicorn-0.12.1.tar.gz
Running gunicorn-0.12.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-gf0pif/gunicorn-0.12.1/egg-dist-tmp-8ToCBM
Adding gunicorn 0.12.1 to easy-install.pth file
Installing gunicorn_paster script to /home/nicolas314/webtests/app/bin
Installing gunicorn script to /home/nicolas314/webtests/app/bin
Installing gunicorn_django script to /home/nicolas314/webtests/app/bin
Installed /home/nicolas314/webtests/app/lib/python2.6/site-packages/gunicorn-0.12.1-py2.6.egg
Processing dependencies for gunicorn
Finished processing dependencies for gunicorn

Now let us write a default Python webapp with two classes: immediate responds immediately to /im requests and delayed simulates a long-running task responding to /de: sleeps for 10 seconds and returns.

hello.py contains:

import time, web
class immediate:
    def GET(self):
        return 'immediate'
class delayed:
    def GET(self):
        time.sleep(10)
        return 'delayed'
urls = ('/im', 'immediate',
            '/de', 'delayed')
application = web.application(urls, globals(), True).wsgifunc()

Startup gunicorn with 10 workers on localhost:

gunicorn -w 10 hello

Open a browser, point one tab to localhost:8080/de and the other one to localhost:8080/im. The delayed task does not impact (directly) immediate responses. QED.

gunicorn has been benchmarked with excellent performance and will take care of all multi-worker stuff for you. Integration with web.py is excellent and painless. Installation is just a couple of commands. Congratulations to the gunicorn team!

References:

Advertisements

Written by nicolas314

Wednesday 6 April 2011 at 11:30 pm

fapws3 + web.py

leave a comment »

Objective: run a web.py application with the fapws3 web server

References:

If you are looking into fast and easy ways to run your web.py-based application, there are many exciting alternatives out there claiming to be both easier to install (easy) and faster (not so easy) than Apache+mod_wsgi. A benchmark of Python web servers summarizes all good candidates today. I decided to give them all a quick try and see what they have to offer. First in line: fapws3

Here is my HelloWorld web.py:

---hello.py---
import web
class hello:
  def GET(self):
    return 'Hello world'
urls = ('/', 'hello')
application = web.application(urls, globals(), True).wsgifunc()

and here is the glu to run it from fapws3:

---run.py---
import hello
from fapws import base
import fapws._evwsgi as evwsgi
if __name__=="__main__":
  evwsgi.start('0.0.0.0', '8080')
  evwsgi.set_base_module(base)
  evwsgi.wsgi_cb(('', hello.application))
  evwsgi.run()

Start the server with python run.py and point your browser to http://localhost:8080 to see it run. Ok, now let us modify a bit our web app: say I have a URL that requires longer computation times. This is simulated here with time.sleep:

import web, time
class immediate:
  def GET(self):
    return 'immediate'
class delayed:
  def GET(self):
    time.sleep(10)
    return 'delayed'
urls = ('/immediate', 'immediate',
           '/delayed', 'delayed')
application = web.application(urls, globals(), True).wsgifunc()

Open two tabs in your browsers, point the first to /immediate and the second one to /delayed. Now reload both… and wait 10 seconds to see /immediate get refreshed. Ouch. One long-running request blocks the whole server.

Issues

  • fapws3 is not threaded and never will be, according to the FAQ
  • fapws3 does not support SSL

No support for multi-threading means that you will have to implement your own manager/worker mechanism for long-running requests. The fapws3 FAQ recommends using many parallel instances and pound for load-balancing and SSL support. WTF?

Now I am left wondering: what could fapws3 possibly be useful for? There are so many more WSGI-compatible web servers with excellent performances, a full thread stack and complete SSL support out of the box, why should I bother with one that lets me do all the work? I probably missed something. Oh well…

Written by nicolas314

Monday 7 March 2011 at 11:39 pm

Fast Python webapp

leave a comment »

Just spent the last few days trying to find the fastest way to put together a Python webapp. Not an easy task, especially since documentation on the topic is really abundant and (I found) rarely self-sufficient. I ended up choosing what I believe is the most straightforward alignment of code to get a Python webapp up and running in minutes, and make it portable to production mode without efforts.

web.py

web.py is the simplest Python framework there is. Straight and to the point: you can program very basic stuff but if you really want to, you can add templates and database and model-view-controller design as you see fit. The basic hello world in web.py would be:

 

!/usr/bin/python
import web

class hello:
    def GET(self):
        return 'Hello world'

urls = ('/', 'hello')
app  = web.application(urls, globals(), True)

if __name__=="__main__":
    app.run()

 

Cannot get simpler than that! URLs are mapped to callables by regexes, which gives you perfect flexibility for URL design. Your classes can implement different methods for GET and POST, keeping closer to the real REST philosophy. Without having to install any further software you can immediately test your app by running:

 

python hello.py

 

web.py is friendly enough to embed its own (pure-Python) web server for test purposes. Debug mode is also automatically activated in that mode so you will be able to get usable messages when things go wrong during development.

lighttpd

I have spent a lot of time with lighttpd now, browsing documentation (I even bought the book!), parsing the source and even participating on their forum. Now is time to get my return on investment. I just found out that lighttpd can launch web.py-based apps directly in fastcgi mode without need to write your own boilerplate code to convert fastcgi to wsgi. Here is a minimal lighttpd configuration that just works:

 

server.port = 8080
server.modules = ("mod_fastcgi", "mod_rewrite")
server.document-root = "/home/www/"
fastcgi.server = ( "hello.py" =>
    (( "socket" => "/tmp/fastcgi.socket",
       "bin-path" => "/home/www/hello.py",
       "max-procs" => 5
    ))
    )
url.rewrite-once = (
    "^/sta/(.*)$" => "/sta/$1",
    "^/(.*)$" => "/hello.py/$1"
)

 

The above specifies a fastcgi handler called ‘hello.py’ that is always called thanks to the last rewrite rule, except for stuff located in /sta which is directly served by lighttpd. /sta is where you are going to store your served static content like images and css.

In production you can launch a bunch of lighttpd front-ends and configure them to talk to a fastcgi app possibly located on another server or farm of servers.

Took me quite a while to converge to this simple solution. Other paths I reviewed where:

  • Apache+mod_wsgi: too heavy
  • cherokee+uwsgi: cherokee is really nice but uwsgi is an ugly duckling
  • lighttpd+SCGI+flup+cherrypy: works but heavy and boilerplate code is ugly and un-maintainable

Not saying the other solutions are bad, they are just not as straightforward.

Written by nicolas314

Thursday 7 October 2010 at 11:24 pm

Posted in python, webapp

Tagged with , , ,