Some may be confused: Isn't tornado advertised that asynchronous non-blocking solves the 10 k problem? However, I found that not torando is not good, but you used it wrong. For example, you recently discovered one thing: a certain network
Preface
Some may be confused: Isn't tornado advertised that asynchronous non-blocking solves the 10 k problem? But I found that not torando is bad, but you used it wrong. for example, a website is slow to open pages, and the CPU/memory of the server is normal. the network status is also good. later, I found that there were a lot of requests to access the back-end database, and there was a rest service for the MongoDB Database Service API. however, its tornado is wrong, and the problem is studied step by step:
Description
In the following example, there are two URLs: one is a time-consuming request, and the other is a request that can or needs to be returned immediately. I think even if I am not familiar with the technology, in principle, he hopes that his access requests will not be affected and will not be affected by other users' requests.
#! /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 5 S ")
Class justnowhandler (tornado. Web. requesthandler ):
Def get (Self ):
Self. Write ("I hope just now see you ")
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 ()
Assume that you access http: // localhost: 8000/sleep first by using a page request or tools such as httpie and curl, and then access http: // localhost: 8000/justnow. you will find that the/jsunow request that can be returned immediately will be blocked until the/sleep request is complete.
Why? Why is my request blocked by/sleep requests? If our Web requests are fast enough at ordinary times, we may not be aware of this problem, but in fact there are often some time-consuming processes, which means that the application is effectively locked until the processing ends.
Does this remind you of @ tornado. Web. asynchronous? However, the premise of using this decorator is that you need to execute asynchronous execution for time-consuming execution, such as the preceding time. sleep, you just add the modifier does not work, and it should be noted that tornado closes the client connection when function processing returns by default, but when you use @ tornado. web. when Asynchonous decorator is used, Tornado will never close the connection by itself. close finish ()
Most of our functions are congested. For example, the above time. Sleep actually tornado has 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 5 S ")
Class justnowhandler (tornado. Web. requesthandler ):
Def get (Self ):
Self. Write ("I hope just now see you ")
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 is a new tornado. gen. coroutine, which is a new coroutine after 3.0. The previous method is to use callback. Let's look at 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 5 S ")
Self. Finish ()
Callback is used, but the new decorator allows us to achieve the same effect through yield: You click/justnow after opening/sleep, and the requests of justnow are immediately returned and will not be affected. however, with the asynchronous decorator, your time-consuming functions also need to be asynchronous.
The above are all meaningless examples. The following is a bit useful: Read the MongoDB database data, and then write the data in the front end by row.
#! /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 python driver for MongoDB 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 the cursor of 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 row to be blocked. My TT set has some data and has no index.
Cursor = dB. tt. Find (). Sort ([('A',-1)])
# The asynchronous non-blocking execution of this branch does not affect other page requests
While (yield cursor. fetch_next ):
Message = cursor. next_object ()
Self. Write ('<li> % S </LI>' % message ['a'])
Self. Write ('</ul> ')
Self. Finish ()
Def _ on_response (self, message, error ):
If error:
Raise tornado. Web. httperror (500, error)
Elif message:
For I in message:
Self. Write ('<li> % S </LI>' % I ['a'])
Else:
Self. Write ('</ul> ')
Self. Finish ()
Class justnowhandler (basehandler ):
Def get (Self ):
Self. Write ("I hope just now see you ")
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 suggested why this time-consuming item cannot be asynchronously thrown to a tool for execution without blocking my requests? Okay, I also thought of celery. GitHub has this thing: tornado-celery.
To execute the following program, you must first 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 Tornado. gen. Task parameter is the function to be executed. The Parameter
Yield tornado. gen. task (tasks. Sleep. apply_async, argS = [5])
Self. Write ("when I sleep 5 S ")
Self. Finish ()
Class justnowhandler (tornado. Web. requesthandler ):
Def get (Self ):
Self. Write ("I hope just now see you ")
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 ()
Task is a celery task definition file, which contains the time. Sleep function.
Import time
From celery import celery
Celery = celery ("Tasks", broker = "amqp: // Guest: [email protected]: 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 celelry worker (or how do you execute your task? Must be removed by a consumer ):
Celery-A tasks worker -- loglevel = info
However, the problem here may also be very serious: Our asynchronous non-blocking depends on celery, or the length of this queue. If there are many tasks, we need to wait and the efficiency is very low. is there a way to convert my synchronous blocking function to asynchronous (or be understood and recognized by tornado decorators?
#! /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 is included in python3 and needs to install sudo Pip install futures in python2.
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 a local variable rather than a global variable.
@ Tornado. Web. asynchronous
@ Tornado. gen. coroutine
Def get (Self ):
# If the asynchronous method you run will return the value to be called again, you can (just for demonstration). Otherwise, you can directly use yield.
Res = yield self. Sleep ()
Self. Write ("when I sleep % 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 see you ")
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 ()
Tornado asynchronous request non-blocking