• United States+1
  • United Kingdom+44
  • Afghanistan (‫افغانستان‬‎)+93
  • Albania (Shqipëri)+355
  • Algeria (‫الجزائر‬‎)+213
  • American Samoa+1684
  • Andorra+376
  • Angola+244
  • Anguilla+1264
  • Antigua and Barbuda+1268
  • Argentina+54
  • Armenia (Հայաստան)+374
  • Aruba+297
  • Australia+61
  • Austria (Österreich)+43
  • Azerbaijan (Azərbaycan)+994
  • Bahamas+1242
  • Bahrain (‫البحرين‬‎)+973
  • Bangladesh (বাংলাদেশ)+880
  • Barbados+1246
  • Belarus (Беларусь)+375
  • Belgium (België)+32
  • Belize+501
  • Benin (Bénin)+229
  • Bermuda+1441
  • Bhutan (འབྲུག)+975
  • Bolivia+591
  • Bosnia and Herzegovina (Босна и Херцеговина)+387
  • Botswana+267
  • Brazil (Brasil)+55
  • British Indian Ocean Territory+246
  • British Virgin Islands+1284
  • Brunei+673
  • Bulgaria (България)+359
  • Burkina Faso+226
  • Burundi (Uburundi)+257
  • Cambodia (កម្ពុជា)+855
  • Cameroon (Cameroun)+237
  • Canada+1
  • Cape Verde (Kabu Verdi)+238
  • Caribbean Netherlands+599
  • Cayman Islands+1345
  • Central African Republic (République centrafricaine)+236
  • Chad (Tchad)+235
  • Chile+56
  • China (中国)+86
  • Christmas Island+61
  • Cocos (Keeling) Islands+61
  • Colombia+57
  • Comoros (‫جزر القمر‬‎)+269
  • Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)+243
  • Congo (Republic) (Congo-Brazzaville)+242
  • Cook Islands+682
  • Costa Rica+506
  • Côte d’Ivoire+225
  • Croatia (Hrvatska)+385
  • Cuba+53
  • Curaçao+599
  • Cyprus (Κύπρος)+357
  • Czech Republic (Česká republika)+420
  • Denmark (Danmark)+45
  • Djibouti+253
  • Dominica+1767
  • Dominican Republic (República Dominicana)+1
  • Ecuador+593
  • Egypt (‫مصر‬‎)+20
  • El Salvador+503
  • Equatorial Guinea (Guinea Ecuatorial)+240
  • Eritrea+291
  • Estonia (Eesti)+372
  • Ethiopia+251
  • Falkland Islands (Islas Malvinas)+500
  • Faroe Islands (Føroyar)+298
  • Fiji+679
  • Finland (Suomi)+358
  • France+33
  • French Guiana (Guyane française)+594
  • French Polynesia (Polynésie française)+689
  • Gabon+241
  • Gambia+220
  • Georgia (საქართველო)+995
  • Germany (Deutschland)+49
  • Ghana (Gaana)+233
  • Gibraltar+350
  • Greece (Ελλάδα)+30
  • Greenland (Kalaallit Nunaat)+299
  • Grenada+1473
  • Guadeloupe+590
  • Guam+1671
  • Guatemala+502
  • Guernsey+44
  • Guinea (Guinée)+224
  • Guinea-Bissau (Guiné Bissau)+245
  • Guyana+592
  • Haiti+509
  • Honduras+504
  • Hong Kong (香港)+852
  • Hungary (Magyarország)+36
  • Iceland (Ísland)+354
  • India (भारत)+91
  • Indonesia+62
  • Iran (‫ایران‬‎)+98
  • Iraq (‫العراق‬‎)+964
  • Ireland+353
  • Isle of Man+44
  • Israel (‫ישראל‬‎)+972
  • Italy (Italia)+39
  • Jamaica+1876
  • Japan (日本)+81
  • Jersey+44
  • Jordan (‫الأردن‬‎)+962
  • Kazakhstan (Казахстан)+7
  • Kenya+254
  • Kiribati+686
  • Kosovo+383
  • Kuwait (‫الكويت‬‎)+965
  • Kyrgyzstan (Кыргызстан)+996
  • Laos (ລາວ)+856
  • Latvia (Latvija)+371
  • Lebanon (‫لبنان‬‎)+961
  • Lesotho+266
  • Liberia+231
  • Libya (‫ليبيا‬‎)+218
  • Liechtenstein+423
  • Lithuania (Lietuva)+370
  • Luxembourg+352
  • Macau (澳門)+853
  • Macedonia (FYROM) (Македонија)+389
  • Madagascar (Madagasikara)+261
  • Malawi+265
  • Malaysia+60
  • Maldives+960
  • Mali+223
  • Malta+356
  • Marshall Islands+692
  • Martinique+596
  • Mauritania (‫موريتانيا‬‎)+222
  • Mauritius (Moris)+230
  • Mayotte+262
  • Mexico (México)+52
  • Micronesia+691
  • Moldova (Republica Moldova)+373
  • Monaco+377
  • Mongolia (Монгол)+976
  • Montenegro (Crna Gora)+382
  • Montserrat+1664
  • Morocco (‫المغرب‬‎)+212
  • Mozambique (Moçambique)+258
  • Myanmar (Burma) (မြန်မာ)+95
  • Namibia (Namibië)+264
  • Nauru+674
  • Nepal (नेपाल)+977
  • Netherlands (Nederland)+31
  • New Caledonia (Nouvelle-Calédonie)+687
  • New Zealand+64
  • Nicaragua+505
  • Niger (Nijar)+227
  • Nigeria+234
  • Niue+683
  • Norfolk Island+672
  • North Korea (조선 민주주의 인민 공화국)+850
  • Northern Mariana Islands+1670
  • Norway (Norge)+47
  • Oman (‫عُمان‬‎)+968
  • Pakistan (‫پاکستان‬‎)+92
  • Palau+680
  • Palestine (‫فلسطين‬‎)+970
  • Panama (Panamá)+507
  • Papua New Guinea+675
  • Paraguay+595
  • Peru (Perú)+51
  • Philippines+63
  • Poland (Polska)+48
  • Portugal+351
  • Puerto Rico+1
  • Qatar (‫قطر‬‎)+974
  • Réunion (La Réunion)+262
  • Romania (România)+40
  • Russia (Россия)+7
  • Rwanda+250
  • Saint Barthélemy (Saint-Barthélemy)+590
  • Saint Helena+290
  • Saint Kitts and Nevis+1869
  • Saint Lucia+1758
  • Saint Martin (Saint-Martin (partie française))+590
  • Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)+508
  • Saint Vincent and the Grenadines+1784
  • Samoa+685
  • San Marino+378
  • São Tomé and Príncipe (São Tomé e Príncipe)+239
  • Saudi Arabia (‫المملكة العربية السعودية‬‎)+966
  • Senegal (Sénégal)+221
  • Serbia (Србија)+381
  • Seychelles+248
  • Sierra Leone+232
  • Singapore+65
  • Sint Maarten+1721
  • Slovakia (Slovensko)+421
  • Slovenia (Slovenija)+386
  • Solomon Islands+677
  • Somalia (Soomaaliya)+252
  • South Africa+27
  • South Korea (대한민국)+82
  • South Sudan (‫جنوب السودان‬‎)+211
  • Spain (España)+34
  • Sri Lanka (ශ්‍රී ලංකාව)+94
  • Sudan (‫السودان‬‎)+249
  • Suriname+597
  • Svalbard and Jan Mayen+47
  • Swaziland+268
  • Sweden (Sverige)+46
  • Switzerland (Schweiz)+41
  • Syria (‫سوريا‬‎)+963
  • Taiwan (台灣)+886
  • Tajikistan+992
  • Tanzania+255
  • Thailand (ไทย)+66
  • Timor-Leste+670
  • Togo+228
  • Tokelau+690
  • Tonga+676
  • Trinidad and Tobago+1868
  • Tunisia (‫تونس‬‎)+216
  • Turkey (Türkiye)+90
  • Turkmenistan+993
  • Turks and Caicos Islands+1649
  • Tuvalu+688
  • U.S. Virgin Islands+1340
  • Uganda+256
  • Ukraine (Україна)+380
  • United Arab Emirates (‫الإمارات العربية المتحدة‬‎)+971
  • United Kingdom+44
  • United States+1
  • Uruguay+598
  • Uzbekistan (Oʻzbekiston)+998
  • Vanuatu+678
  • Vatican City (Città del Vaticano)+39
  • Venezuela+58
  • Vietnam (Việt Nam)+84
  • Wallis and Futuna+681
  • Western Sahara (‫الصحراء الغربية‬‎)+212
  • Yemen (‫اليمن‬‎)+967
  • Zambia+260
  • Zimbabwe+263
  • Åland Islands+358
Thanks! We'll be in touch in the next 12 hours
Oops! Something went wrong while submitting the form.

A Beginner’s Guide to Python Tornado

Shubham Hapse

Full-stack Development

The web is a big place now. We need to support thousands of clients at a time, and here comes Tornado. Tornado is a Python web framework and asynchronous network library, originally developed at FriendFreed.

Tornado uses non-blocking network-io. Due to this, it can handle thousands of active server connections. It is a saviour for applications where long polling and a large number of active connections are maintained.

Tornado is not like most Python frameworks. It's not based on WSGI, while it supports some features of WSGI using module `tornado.wsgi`. It uses an event loop design that makes Tornado request execution faster.  

What is Synchronous Program?

A function blocks, performs its computation, and returns, once done . A function may block for many reasons: network I/O, disk I/O, mutexes, etc.

Application performance depends on how efficiently application uses CPU cycles, that's why blocking statements/calls must be taken seriously. Consider password hashing functions like bcrypt, which by design use hundreds of milliseconds of CPU time, far more than a typical network or disk access. As the CPU is not idle, there is no need to go for asynchronous functions.

A function can be blocking in one, and non-blocking in others. In the context of Tornado, we generally consider blocking due to network I/O and disk, although all kinds of blocking need to be minimized.

What is Asynchronous Program?

1) Single-threaded architecture:

    Means, it can't do computation-centric tasks parallely.

2) I/O concurrency:

    It can handover IO tasks to the operating system and continue to the next task to achieve parallelism.

3) epoll/ kqueue:

    Underline system-related construct that allows an application to get events on a file descriptor or I/O specific tasks.

4) Event loop:

    It uses epoll or kqueue to check if any event has happened, and executes callback that is waiting for those network events.

Asynchronous vs Synchronous Web Framework:

In case of synchronous model, each request or task is transferred to thread or routing, and as it finishes, the result is handed over to the caller. Here, managing things are easy, but creating new threads is too much overhead.

On the other hand, in Asynchronous framework, like Node.js, there is a single-threaded model, so very less overhead, but it has complexity.

Let's imagine thousands of requests coming through and a server uses event loop and callback. Now, until request gets processed, it has to efficiently store and manage the state of that request to map callback result to the actual client.

Node.js vs Tornado

Most of these comparison points are tied to actual programming language and not the framework: 

  • Node.js has one big advantage that all of its libraries are Async. In Python, there are lots of available packages, but very few of them are asynchronous
  • As Node.js is JavaScript runtime, and we can use JS for both front and back-end, developers can keep only one codebase and share the same utility library
  • Google’s V8 engine makes Node.js faster than Tornado. But a lot of Python libraries are written in C and can be faster alternatives.

A Simple 'Hello World' Example

import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()

Note: This example does not use any asynchronous feature.

Using AsyncHTTPClient module, we can do REST call asynchronously.

from tornado.httpclient import AsyncHTTPClient
from tornado import gen
@gen.coroutine
def async_fetch_gen(url):
http_client = AsyncHTTPClient()
response = yield http_client.fetch(url)
raise gen.Return(response.body)

As you can see `yield http_client.fetch(url)` will run as a coroutine.

Complex Example of Tornado Async

Please have a look at Asynchronous Request handler.

WebSockets Using Tornado:

Tornado has built-in package for WebSockets that can be easily used with coroutines to achieve concurrency, here is one example:

import logging
import tornado.escape
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.websocket
from tornado.options import define, options
from tornado.httpserver import HTTPServer
define("port", default=8888, help="run on the given port", type=int)
# queue_size = 1
# producer_num_items = 5
# q = queues.Queue(queue_size)
def isPrime(num):
"""
Simple worker but mostly IO/network call
"""
if num > 1:
for i in range(2, num // 2):
if (num % i) == 0:
return ("is not a prime number")
else:
return("is a prime number")
else:
return ("is not a prime number")
class Application(tornado.web.Application):
def __init__(self):
handlers = [(r"/chatsocket", TornadoWebSocket)]
super(Application, self).__init__(handlers)
class TornadoWebSocket(tornado.websocket.WebSocketHandler):
clients = set()
# enable cross domain origin
def check_origin(self, origin):
return True
def open(self):
TornadoWebSocket.clients.add(self)
# when client closes connection
def on_close(self):
TornadoWebSocket.clients.remove(self)
@classmethod
def send_updates(cls, producer, result):
for client in cls.clients:
# check if result is mapped to correct sender
if client == producer:
try:
client.write_message(result)
except:
logging.error("Error sending message", exc_info=True)
def on_message(self, message):
try:
num = int(message)
except ValueError:
TornadoWebSocket.send_updates(self, "Invalid input")
return
TornadoWebSocket.send_updates(self, isPrime(num))
def start_websockets():
tornado.options.parse_command_line()
app = Application()
server = HTTPServer(app)
server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
if __name__ == "__main__":
start_websockets()
view raw websockets.py hosted with ❤ by GitHub

One can use a WebSocket client application to connect to the server, message can be any integer. After processing, the client receives the result if the integer is prime or not.  
Here is one more example of actual async features of Tornado. Many will find it similar to Golang’s Goroutine and channels.

In this example, we can start worker(s) and they will listen to the 'tornado.queue'. This queue is asynchronous and very similar to the asyncio package.

# Example 1
from tornado import gen, queues
from tornado.ioloop import IOLoop
@gen.coroutine
def consumer(queue, num_expected):
for _ in range(num_expected):
# heavy I/O or network task
print('got: %s' % (yield queue.get()))
@gen.coroutine
def producer(queue, num_items):
for i in range(num_items):
print('putting %s' % i)
yield queue.put(i)
@gen.coroutine
def main():
"""
Starts producer and consumer and wait till they finish
"""
yield [producer(q, producer_num_items), consumer(q, producer_num_items)]
queue_size = 1
producer_num_items = 5
q = queues.Queue(queue_size)
results = IOLoop.current().run_sync(main)
# Output:
# putting 0
# putting 1
# got: 0
# got: 1
# putting 2
# putting 3
# putting 4
# got: 2
# got: 3
# got: 4
# Example 2
# Condition
# A condition allows one or more coroutines to wait until notified.
from tornado import gen
from tornado.ioloop import IOLoop
from tornado.locks import Condition
my_condition = Condition()
@gen.coroutine
def waiter():
print("I'll wait right here")
yield my_condition.wait()
print("Received notification now doing my things")
@gen.coroutine
def notifier():
yield gen.sleep(60)
print("About to notify")
my_condition.notify()
print("Done notifying")
@gen.coroutine
def runner():
# Wait for waiter() and notifier() in parallel
yield([waiter(), notifier()])
results = IOLoop.current().run_sync(runner)
# output:
# I'll wait right here
# About to notify
# Done notifying
# Received notification now doing my things

Conclusion

1) Asynchronous frameworks are not much of use when most of the computations are CPU centric and not I/O.

2) Due to a single thread per core model and event loop, it can manage thousands of active client connections.

3) Many say Django is too big, Flask is too small, and Tornado is just right:)

Get the latest engineering blogs delivered straight to your inbox.
No spam. Only expert insights.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings

A Beginner’s Guide to Python Tornado

The web is a big place now. We need to support thousands of clients at a time, and here comes Tornado. Tornado is a Python web framework and asynchronous network library, originally developed at FriendFreed.

Tornado uses non-blocking network-io. Due to this, it can handle thousands of active server connections. It is a saviour for applications where long polling and a large number of active connections are maintained.

Tornado is not like most Python frameworks. It's not based on WSGI, while it supports some features of WSGI using module `tornado.wsgi`. It uses an event loop design that makes Tornado request execution faster.  

What is Synchronous Program?

A function blocks, performs its computation, and returns, once done . A function may block for many reasons: network I/O, disk I/O, mutexes, etc.

Application performance depends on how efficiently application uses CPU cycles, that's why blocking statements/calls must be taken seriously. Consider password hashing functions like bcrypt, which by design use hundreds of milliseconds of CPU time, far more than a typical network or disk access. As the CPU is not idle, there is no need to go for asynchronous functions.

A function can be blocking in one, and non-blocking in others. In the context of Tornado, we generally consider blocking due to network I/O and disk, although all kinds of blocking need to be minimized.

What is Asynchronous Program?

1) Single-threaded architecture:

    Means, it can't do computation-centric tasks parallely.

2) I/O concurrency:

    It can handover IO tasks to the operating system and continue to the next task to achieve parallelism.

3) epoll/ kqueue:

    Underline system-related construct that allows an application to get events on a file descriptor or I/O specific tasks.

4) Event loop:

    It uses epoll or kqueue to check if any event has happened, and executes callback that is waiting for those network events.

Asynchronous vs Synchronous Web Framework:

In case of synchronous model, each request or task is transferred to thread or routing, and as it finishes, the result is handed over to the caller. Here, managing things are easy, but creating new threads is too much overhead.

On the other hand, in Asynchronous framework, like Node.js, there is a single-threaded model, so very less overhead, but it has complexity.

Let's imagine thousands of requests coming through and a server uses event loop and callback. Now, until request gets processed, it has to efficiently store and manage the state of that request to map callback result to the actual client.

Node.js vs Tornado

Most of these comparison points are tied to actual programming language and not the framework: 

  • Node.js has one big advantage that all of its libraries are Async. In Python, there are lots of available packages, but very few of them are asynchronous
  • As Node.js is JavaScript runtime, and we can use JS for both front and back-end, developers can keep only one codebase and share the same utility library
  • Google’s V8 engine makes Node.js faster than Tornado. But a lot of Python libraries are written in C and can be faster alternatives.

A Simple 'Hello World' Example

import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()

Note: This example does not use any asynchronous feature.

Using AsyncHTTPClient module, we can do REST call asynchronously.

from tornado.httpclient import AsyncHTTPClient
from tornado import gen
@gen.coroutine
def async_fetch_gen(url):
http_client = AsyncHTTPClient()
response = yield http_client.fetch(url)
raise gen.Return(response.body)

As you can see `yield http_client.fetch(url)` will run as a coroutine.

Complex Example of Tornado Async

Please have a look at Asynchronous Request handler.

WebSockets Using Tornado:

Tornado has built-in package for WebSockets that can be easily used with coroutines to achieve concurrency, here is one example:

import logging
import tornado.escape
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.websocket
from tornado.options import define, options
from tornado.httpserver import HTTPServer
define("port", default=8888, help="run on the given port", type=int)
# queue_size = 1
# producer_num_items = 5
# q = queues.Queue(queue_size)
def isPrime(num):
"""
Simple worker but mostly IO/network call
"""
if num > 1:
for i in range(2, num // 2):
if (num % i) == 0:
return ("is not a prime number")
else:
return("is a prime number")
else:
return ("is not a prime number")
class Application(tornado.web.Application):
def __init__(self):
handlers = [(r"/chatsocket", TornadoWebSocket)]
super(Application, self).__init__(handlers)
class TornadoWebSocket(tornado.websocket.WebSocketHandler):
clients = set()
# enable cross domain origin
def check_origin(self, origin):
return True
def open(self):
TornadoWebSocket.clients.add(self)
# when client closes connection
def on_close(self):
TornadoWebSocket.clients.remove(self)
@classmethod
def send_updates(cls, producer, result):
for client in cls.clients:
# check if result is mapped to correct sender
if client == producer:
try:
client.write_message(result)
except:
logging.error("Error sending message", exc_info=True)
def on_message(self, message):
try:
num = int(message)
except ValueError:
TornadoWebSocket.send_updates(self, "Invalid input")
return
TornadoWebSocket.send_updates(self, isPrime(num))
def start_websockets():
tornado.options.parse_command_line()
app = Application()
server = HTTPServer(app)
server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
if __name__ == "__main__":
start_websockets()
view raw websockets.py hosted with ❤ by GitHub

One can use a WebSocket client application to connect to the server, message can be any integer. After processing, the client receives the result if the integer is prime or not.  
Here is one more example of actual async features of Tornado. Many will find it similar to Golang’s Goroutine and channels.

In this example, we can start worker(s) and they will listen to the 'tornado.queue'. This queue is asynchronous and very similar to the asyncio package.

# Example 1
from tornado import gen, queues
from tornado.ioloop import IOLoop
@gen.coroutine
def consumer(queue, num_expected):
for _ in range(num_expected):
# heavy I/O or network task
print('got: %s' % (yield queue.get()))
@gen.coroutine
def producer(queue, num_items):
for i in range(num_items):
print('putting %s' % i)
yield queue.put(i)
@gen.coroutine
def main():
"""
Starts producer and consumer and wait till they finish
"""
yield [producer(q, producer_num_items), consumer(q, producer_num_items)]
queue_size = 1
producer_num_items = 5
q = queues.Queue(queue_size)
results = IOLoop.current().run_sync(main)
# Output:
# putting 0
# putting 1
# got: 0
# got: 1
# putting 2
# putting 3
# putting 4
# got: 2
# got: 3
# got: 4
# Example 2
# Condition
# A condition allows one or more coroutines to wait until notified.
from tornado import gen
from tornado.ioloop import IOLoop
from tornado.locks import Condition
my_condition = Condition()
@gen.coroutine
def waiter():
print("I'll wait right here")
yield my_condition.wait()
print("Received notification now doing my things")
@gen.coroutine
def notifier():
yield gen.sleep(60)
print("About to notify")
my_condition.notify()
print("Done notifying")
@gen.coroutine
def runner():
# Wait for waiter() and notifier() in parallel
yield([waiter(), notifier()])
results = IOLoop.current().run_sync(runner)
# output:
# I'll wait right here
# About to notify
# Done notifying
# Received notification now doing my things

Conclusion

1) Asynchronous frameworks are not much of use when most of the computations are CPU centric and not I/O.

2) Due to a single thread per core model and event loop, it can manage thousands of active client connections.

3) Many say Django is too big, Flask is too small, and Tornado is just right:)

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings