Objective
Perhaps a classmate is puzzled: Tornado not advertised asynchronous non-blocking solve 10K problem? But I found not torando bad, but you use the wrong. For example, recently found a thing: a Web site open page is slow, the server cpu/memory is OK. The network is also in good condition. Later found that the open page would have a lot of requests for back-end database access, and there is a MongoDB database service API for rest services. But its tornado is wrong, step-by-step to study the problem:
Description
The following examples have 2 URLs, one is a time-consuming request, one is a request that can or should be returned immediately, I think even if a person is not familiar with the technology, from the point of view of the user, he hopes that his visit will not affect the request and will not be affected by other people's request
#!/bin/env python
Import Tornado.httpserver
Import Tornado.ioloop
Import Tornado.options
Import Tornado.web
Import Tornado.httpclient
Import time
From tornado.options import define, Options
Define ("Port", default=8000, help= "run on the given port", Type=int)
Class Sleephandler (Tornado.web.RequestHandler):
def get (self):
Time.sleep (5)
Self.write ("When I Sleep 5s")
Class Justnowhandler (Tornado.web.RequestHandler):
def get (self):
Self.write ("I hope just now")
if __name__ = = "__main__":
Tornado.options.parse_command_line ()
App = Tornado.web.Application (handlers=[
(r "/sleep", Sleephandler), (R "/justnow", Justnowhandler)])
Http_server = Tornado.httpserver.HTTPServer (APP)
Http_server.listen (Options.port)
Tornado.ioloop.IOLoop.instance (). Start ()
If you use a page request or use a tool such as Httpie,curl to access Http://localhost:8000/sleep first, then visit Http://localhost:8000/justnow. You will find that you can return immediately. The Jsutnow request will be blocked until the/sleep request is finished before returning.
Why is this? Why is my request blocked by a/sleep request? If our web requests are usually fast enough we may not be aware of this problem, but in fact there are often time-consuming processes that mean that the application is effectively locked until the processing is complete.
Is it time you remembered @tornado.web.asynchronous this decorator? But the premise of using this decorator is that you need to take time to execute asynchronously, such as the above time.sleep, you just add the adorner is not functional, and it is important to note that tornado by default when the function processing returns, the client's connection is closed. But when you use the @tornado.web.asynchonous adorner, Tornado never close the connection yourself, and you need an explicit self.finish () to close
Most of our functions are blocked, such as the above Time.sleep actually tornado an asynchronous implementation:
#!/bin/env python
Import Tornado.httpserver
Import Tornado.ioloop
Import Tornado.options
Import Tornado.web
Import Tornado.gen
Import Tornado.httpclient
Import Tornado.concurrent
Import Tornado.ioloop
Import time
From tornado.options import define, Options
Define ("Port", default=8000, help= "run on the given port", Type=int)
Class Sleephandler (Tornado.web.RequestHandler):
@tornado. web.asynchronous
@tornado. Gen.coroutine
def get (self):
Yield Tornado.gen.Task (Tornado.ioloop.IOLoop.instance (). Add_timeout, Time.time () + 5)
Self.write ("When I Sleep 5s")
Class Justnowhandler (Tornado.web.RequestHandler):
def get (self):
Self.write ("I hope just now")
if __name__ = = "__main__":
Tornado.options.parse_command_line ()
App = Tornado.web.Application (handlers=[
(r "/sleep", Sleephandler), (R "/justnow", Justnowhandler)])
Http_server = Tornado.httpserver.HTTPServer (APP)
Http_server.listen (Options.port)
Tornado.ioloop.IOLoop.instance (). Start ()
Here's a new tornado.gen.coroutine decorator, and Coroutine is the decorator that was added after 3.0. The previous approach was to use a callback, or see my example:
Class Sleephandler (Tornado.web.RequestHandler):
@tornado. web.asynchronous
def get (self):
Tornado.ioloop.IOLoop.instance (). Add_timeout (Time.time () + 5, Callback=self.on_response)
def on_response (self):
Self.write ("When I Sleep 5s")
Self.finish ()
Callback is used, but the new adorner allows us to achieve the same effect with yield: After you open/sleep and then click/justnow, Justnow requests are immediately returned unaffected. But with the asynchronous decorator, your time-consuming function also needs to perform asynchronous
What you just said is meaningless examples, the following is a bit of a use: Read the MongoDB database data, and then the front-end press the line to write out
#!/bin/env python
Import Tornado.httpserver
Import Tornado.ioloop
Import Tornado.options
Import Tornado.web
Import Tornado.gen
Import Tornado.httpclient
Import Tornado.concurrent
Import Tornado.ioloop
Import time
# a MongoDB-produced Python driver that supports asynchronous databases
Import Motor
From tornado.options import define, Options
Define ("Port", default=8000, help= "run on the given port", Type=int)
# DB is actually a cursor in the test database
db = motor. Motorclient (). Open_sync (). Test
Class Sleephandler (Basehandler):
@tornado. web.asynchronous
@tornado. Gen.coroutine
def get (self):
# It takes time for this line to execute or block, my TT collection has some data and no index
cursor = Db.tt.find (). Sort ([(' A ',-1)])
# This part asynchronously non-blocking execution two does not affect other page requests
while (yield Cursor.fetch_next):
message = Cursor.next_object ()
Self.write ('
%s'% message[' a '])
Self.write (")
Self.finish ()
def _on_response (self, message, error):
If error:
Raise Tornado.web.HTTPError ($, error)
Elif message:
For i in message:
Self.write ('
%s'% i[' a '])
Else
Self.write (")
Self.finish ()
Class Justnowhandler (Basehandler):
def get (self):
Self.write ("I hope just now")
if __name__ = = "__main__":
Tornado.options.parse_command_line ()
App = Tornado.web.Application (handlers=[
(r "/sleep", Sleephandler), (R "/justnow", Justnowhandler)])
Http_server = Tornado.httpserver.HTTPServer (APP)
Http_server.listen (Options.port)
Tornado.ioloop.IOLoop.instance (). Start ()
A colleague suggests why this time-consuming thing cannot be asynchronously dropped to a tool to execute without blocking my request? Well, I also thought: celery, just GitHub has this thing: tornado-celery
Execute the following program first you want to install RABBITMQ and celery:
#!/bin/env python
Import Tornado.httpserver
Import Tornado.ioloop
Import Tornado.options
Import Tornado.web
Import Tornado.gen
Import Tornado.httpclient
Import Tcelery, Tasks
Import time
From tornado.options import define, Options
Define ("Port", default=8000, help= "run on the given port", Type=int)
Tcelery.setup_nonblocking_producer ()
Class Sleephandler (Tornado.web.RequestHandler):
@tornado. web.asynchronous
@tornado. Gen.coroutine
def get (self):
# The parameters of the Tornado.gen.Task are: the function to be executed, the parameter
Yield Tornado.gen.Task (Tasks.sleep.apply_async, args=[5])
Self.write ("When I Sleep 5s")
Self.finish ()
Class Justnowhandler (Tornado.web.RequestHandler):
def get (self):
Self.write ("I hope just now")
if __name__ = = "__main__":
Tornado.options.parse_command_line ()
App = Tornado.web.Application (handlers=[
(r "/sleep", Sleephandler), (R "/justnow", Justnowhandler)])
Http_server = Tornado.httpserver.HTTPServer (APP)
Http_server.listen (Options.port)
Tornado.ioloop.IOLoop.instance (). Start ()
The task is a celery-defined file that contains the functions we call time.sleep.
Import time
From celery import celery
celery = celery ("Tasks", broker= "amqp://guest:guest@localhost:5672")
Celery.conf.CELERY_RESULT_BACKEND = "AMQP"
@celery. Task
def sleep (seconds):
Time.sleep (float (seconds))
return seconds
if __name__ = = "__main__":
Celery.start ()
Then start the Celelry worker (or else how do you do it?) must have a consumer to take away):
Celery-a Tasks worker--loglevel=info
But the problem here can also be serious: our asynchronous non-blocking relies on celery, or the length of the queue, and if the task is a lot then it needs to wait and be inefficient. Is there a way to make my sync blocking function asynchronous (or tornado to understand and recognize it)?
#!/bin/env python
Import Tornado.httpserver
Import Tornado.ioloop
Import Tornado.options
Import Tornado.web
Import Tornado.httpclient
Import Tornado.gen
From tornado.concurrent import Run_on_executor
# This concurrent library in Python3 comes with python2 need to install sudo pip install futures
From concurrent.futures import Threadpoolexecutor
Import time
From tornado.options import define, Options
Define ("Port", default=8000, help= "run on the given port", Type=int)
Class Sleephandler (Tornado.web.RequestHandler):
Executor = Threadpoolexecutor (2)
#executor is that local variables are not global
@tornado. web.asynchronous
@tornado. Gen.coroutine
def get (self):
# If you execute the asynchronous return value will be called again (just for demonstration), otherwise direct yield is OK
res = yield Self.sleep ()
Self.write ("When I sleep%s S"% res)
Self.finish ()
@run_on_executor
def sleep (self):
Time.sleep (5)
Return 5
Class Justnowhandler (Tornado.web.RequestHandler):
def get (self):
Self.write ("I hope just now")
if __name__ = = "__main__":
Tornado.options.parse_command_line ()
App = Tornado.web.Application (handlers=[
(r "/sleep", Sleephandler), (R "/justnow", Justnowhandler)])
Http_server = Tornado.httpserver.HTTPServer (APP)
Http_server.listen (Options.port)
Tornado.ioloop.IOLoop.instance (). Start ()