An asynchronous CherryPy server based on asyncio

CherryPy is a minimalist web application server written in Python. Hundreds of people have relied on it for more than fourteen years now. Recently, I’ve gained interest in the native asynchronous support Python has gained through the implementation of PEP-3156 and PEP-0492. Basically, Python now supports coroutines natively and in a friendly API interface. In addition, thanks to the built-in asyncio module, you can naturally develop concurrent applications.

CherryPy has always used a multi-threaded engine to support concurrent web applications. This may sound surprising, but this is working very well still in 2016. Yet, I love a puzzle and I was interested in making CherryPy run as a set of coroutines rather than a bunch of threads. So a couple of days ago, I set myself on the task to make it happen.

And so here it is, CherryPy on asyncio!

This a branch on my fork, not an official part of the CherryPy project yet.

Now, you can run code like this:

The only differences are:

  • importing cherrypy.async
  • turning page-handlers into coroutines

That is all.

The idea is that the cherrypy.async patches all the internals of CherryPy for you and turn it into an async-aware server.

Note that the code currently runs only on Python 3.5+ as we use the async/await keywords.

Has it been easy?

Turning a project that was not designed for coroutines is not that complicated thanks to the simple interface provided by async/await. However, anytime an I/O operation is performed, it is necessary to transform a call like:

to

This mundane line is sometimes is part of a function call, in that case, you have to overwrite and copy/paste the whole function to change that one line. Mind you, this can’t be avoided because you must also re-declare the function as a coroutine anyway by prefixing it with async.

As usual, the difficulty lies in the entanglement of your code. The more you made simple to comprehend, the simpler and faster it will be to change with confidence.

What was changed?

Mostly the HTTP server, the machinery of CherryPy: the internal engine/bus, the dispatcher, the request handling.

If we can find a way to re-organise the existing code, actually few lines would eventually be changed.

Note, I made the decision not to make this server WSGI aware because I find that rather counter-intuitive with an async-based server somehow.

Is it production ready?

Not at all. It hasn’t been really tested (this will require to re-write many tests so that they play along with coroutines).

It is also, for some unknown reason yet, much slower than the multithreaded version. Profiling will need to be performed.

Still, if you are feeling like testing it:

I don’t know if this code will go further than this but, maybe this will interest the community enough so that it moves forward. This would make CherryPy more suitable for HTTP2 and websockets.


   7 Comments


  1. Ralph Heinkel
      March 21, 2016

    Hi Sylvain,

    very nice work. But also interesting to see that you found such a big performance gap btw the threaded vs the asyncio approach. In case you keep investigating about that it would be nice to learn from you what it was.

    Apart from that I still think cherrypy is a great project, we are still using it for a large lab information management system in a biotech company for many years and I’m still really happy about it!
    Ralph

    • Sylvain Hellegouarch
        March 21, 2016

      Hi Ralph,

      Thanks for the feedback.

      I’m indeed surprised about the drop in performances. I can only assume the code would need a little tuning. If I get the chance, I will profile the code.

      Thanks,

      • Benj
          April 15, 2016

        Hi could it be because any cpu bound operation blocks the entire system? In nodejs this is what happens because it’s totally async but single threaded.

  2. Ching
      March 21, 2016

    Hi Sylvain,

    I learned CherryPy from your book. Now it is amazing that I can study more latest skills (such as asyncio) at your defuze.org.

    What’s more, I highly recommend developers to study https://github.com/Lawouach/Twiseless. Although it looks 5 years old, the modular design of Twiseless is enterprise level, like classic songs of Édith Piaf: timeless and priceless.
    Now I know how to reuse plugins and tools.

    Thank you for your contributions.

    Ching

  3. Bob
      March 30, 2016

    Cool idea! Its a testament to cherrypy’s design that asyncio can be dropped in like this.

  4. Benj
      April 15, 2016

    Hi i think the proper way to do this is keep the theads but release them while waiting for async operation to complete. It’s not a good idea to remove threads. You will loose a lot doing So: any cpu bound task will block the entire system.
    See how they do it in asp.net. Very interesting Read:

    https://msdn.microsoft.com/en-us/magazine/dn802603.aspx

    If cherrypy could do it like this it will be way ahead other frameworks and servers.

Leave a Reply

Your email address will not be published. Required fields are marked *