gevent
tornado
concurrency
python
asynchronous-programming

How to use gevent and tornado in a single application?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Using gevent and Tornado in the same application is possible, but the straightforward answer is that you usually should not try to make them share one event loop. They are built around different concurrency models, so the practical solutions are either to choose one stack for the whole service or to isolate the gevent and Tornado parts into separate processes that communicate over a clear boundary.

Why combining them is awkward

gevent works by monkey-patching blocking I/O and scheduling greenlets cooperatively. Tornado is built around its own non-blocking event loop and explicit asynchronous programming style. Both want to define how network operations behave, and that is where the trouble starts.

The problem is not that they cannot exist in one deployment. The problem is that forcing them to run as if they were one runtime usually creates subtle bugs around sockets, timeouts, DNS, and third-party libraries.

The safest pattern is process isolation

If you already have a Tornado web application and some gevent-friendly code, the cleanest design is usually to run them as separate services. Let Tornado own the HTTP interface and let the gevent side handle the work it is good at, such as high-volume cooperative network clients.

A minimal example looks like this:

python
1# tornado_app.py
2import tornado.ioloop
3import tornado.web
4import httpx
5
6class MainHandler(tornado.web.RequestHandler):
7    async def get(self):
8        response = httpx.get("http://127.0.0.1:9001/work")
9        self.write({"worker_response": response.text})
10
11app = tornado.web.Application([(r"/", MainHandler)])
12app.listen(8888)
13tornado.ioloop.IOLoop.current().start()
python
1# gevent_worker.py
2from gevent import monkey
3monkey.patch_all()
4
5from gevent.pywsgi import WSGIServer
6
7def app(environ, start_response):
8    start_response("200 OK", [("Content-Type", "text/plain")])
9    return [b"done"]
10
11WSGIServer(("127.0.0.1", 9001), app).serve_forever()

This is still one overall system, but each runtime keeps control over its own event model.

If you must keep one process, minimize overlap

Sometimes a full separation is not realistic right away. If both libraries must exist in one process, keep the boundary narrow. Do not monkey-patch late, do not let both frameworks compete for the same socket layer, and do not assume every third-party library behaves correctly after patching.

In practice, that usually means one of the frameworks is only a small compatibility island, not a peer runtime.

Often the better answer is to replace one of them

A lot of teams asking this question are really trying to preserve legacy code during a migration. In that situation, a better end state is usually:

  • Tornado plus asyncio for the whole service
  • or gevent plus a WSGI layer for the whole service

Mixing both long-term usually increases operational complexity without adding useful capability.

If you need Tornado's modern async style, port the gevent portion gradually. If you need gevent patching for a legacy dependency, keep it outside the Tornado service boundary.

Watch blocking calls even in the isolated design

Using both frameworks does not magically make blocking code safe. For example, a Tornado handler that calls a blocking HTTP client or a gevent worker using a library that ignores monkey patching can still stall the service.

The main rule stays the same: each side must still respect its own concurrency model.

Use integration boundaries you can test

If you split the runtimes by process, communicate through plain HTTP, a queue, or RPC. That gives you testable interfaces and makes the system easier to reason about. It also makes future migration easier because you can replace one side without rewriting everything at once.

That is a much more maintainable answer than trying to build a fragile hybrid loop.

Common Pitfalls

  • Trying to make gevent and Tornado share one event loop as equal peers.
  • Monkey-patching too late after other networking modules are already imported.
  • Assuming all third-party libraries behave correctly under gevent patching.
  • Mixing blocking calls into Tornado handlers and blaming the framework integration.
  • Treating a migration bridge as a permanent architecture.

Summary

  • 'gevent and Tornado use different concurrency models and do not combine cleanly in one loop.'
  • The safest design is to isolate them into separate processes or services.
  • If both must live in one codebase temporarily, keep the boundary narrow and explicit.
  • Long term, standardizing on one async model is usually the better architecture.
  • Test the boundary between the two runtimes rather than relying on fragile in-process magic.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.