After the release of Tornado3, the concept of coroutine was strengthened, and in asynchronous programming, it replaced the original Gen.engine and became the present gen.coroutine. This decorator is intended to simplify asynchronous programming in tornado. Avoid writing callback functions that make development more consistent with normal logical thinking. A simple example is as follows:
Class Maindhandler (web. RequestHandler):
@asynchronous
@gen. coroutine
Def post (self):
Client = Asynchttpclient ()
RESP = yield Client.fetch (https://api.github.com/users ")
if Resp.code = = 200:
RESP = Escape.json_decode (resp.body)
Self.write (Json.dumps (resp, indent=4, separators= (', ', ': ')))
Else
RESP = {"message": "Error when fetch something"}
Self.write (Json.dumps (resp, indent=4, separators={', ', ': ')))
Self.finish ()
After the yield statement, Ioloop registers the event until RESP returns and resumes execution. This process is asynchronous. Using Json.dumps here instead of using Tornado's escape.json_encode is because when building a restful API, it is often accessed from the browser to get JSON-formatted data. After you format the data using Json.dumps, it is more friendly when the browser side displays the view. The Github API is the user of this style. In fact, Escape.json_encode is a simple package for json.dumps, and when I ask for more versatility with the pull request package, the author's answer to escape is not intended to provide full JSON functionality, and the user can use the JSON module directly.
Gen.coroutine principle
In a previous blog post about the asynchronous nature of using tornado, you must use an asynchronous library. Otherwise, a single process is blocked and does not achieve an asynchronous effect at all. The most common in Tornado asynchronous libraries is the self-contained asynchttpclient, and the OpenID login authentication interface implemented on top of it. Additional asynchronous libraries can be found here. Including the use of more MongoDB driver.
After the 3.0 release, the Gen.coroutine module appears to be more prominent. The Coroutine decorator allows asynchronous programming that would otherwise be callbacks to look like synchronous programming. This is the use of the Send function of the generator in Python. In the generator, the yield keyword tends to be compared to return in the normal function. It can be used as an iterator to return yield results using next (). But there is another use of the generator, which is using the Send method. The result of yield can be assigned to a variable inside the generator, and this value is sent through the external generator client. To give an example:
Def Test_yield ():
Pirnt "Test Yeild"
says = (yield)
Print says
if __name__ = = "__main__":
Client = Test_yield ()
Client.next ()
Client.send ("Hello World")
The output results are as follows:
Test Yeild
Hello World
The function that is already running is suspended until the client calling it uses the Send method, and the original function continues to run. The Gen.coroutine method here is to execute the required operation asynchronously, then wait for the result to return, then send to the original function, the original function will continue to execute, so that the synchronous writing code to achieve the effect of asynchronous execution.
Tornado Asynchronous Programming
Asynchronous programming that uses Coroutine to implement function separation. Specific as follows:
@gen. coroutine
Def post (self):
Client = Asynchttpclient ()
RESP = yield Client.fetch ("https://api.github.com/users")
If resp = = 200:
BODY = Escape.json_decode (resy.body)
Else
BODY = {"message": "Client Fetch Error"}
Logger.error ("Client fetch error%d,%s"% (Resp.code, resp.message))
Self.write (Escape.json_encode (body))
Self.finish ()
This can be changed after the function is changed;
@gen. Coroutime
Def post (self):
RESP = yield GetUser ()
Self.write (RESP)
@gen. coroutine
Def GetUser ():
Client = Asynchttpclient ()
RESP = yield Client.fetch ("https://api.github.com/users")
if Resp.code = = 200:
RESP = Escape.json_decode (resp.body)
Else
RESP = {"message": "Fetch client Error"}
Logger.error ("Client fetch error%d,%s"% (Resp.code, resp.message))
Raise Gen. Return (RESP)
Here, when the asynchronous encapsulated in a function, not like a normal program with the return keyword to return, Gen module provides a Gen. The return method. is achieved through the Raise method. This is also related to the way it is implemented using the generator.
Use Coroutine to run timed tasks
There is one such method in tornado:
Tornado.ioloop.IOLoop.instance (). Add_timeout ()
The method is a non-blocking version of Time.sleep, which accepts two parameters: a length of time and a function. Represents how much time is called after the function. Here it is based on ioloop, so it is non-blocking. This method is more used in long-client connection and callback function programming. But using it to run some timed tasks is helpless. Usually run-time tasks are also not necessary to use to it. But when I was using Heroku, I found that I could only use a simple Web application hosting without registering a credit card. You cannot add timed tasks to run. So I came up with such a method. Here, I mostly use it to crawl data over a period of time through the GitHub API interface. The large self-use method is as follows:
Decorative Device
def sync_loop_call (delta=60 * 1000):
"""
Wait for func down then process add_timeout
"""
def wrap_loop (func):
@wraps (func)
@gen. coroutine
def wrap_func (*args, **kwargs):
Options.logger.info ("function%r start at%d"%
(func.__name__, Int (Time.time ())))
Try
Yield func (*args, **kwargs)
Except Exception, E:
Options.logger.error ("function%r error:%s"%
(func.__name__, E))
Options.logger.info ("Function%r end at%d"%
(func.__name__, Int (Time.time ())))
Tornado.ioloop.IOLoop.instance (). Add_timeout (
Datetime.timedelta (Milliseconds=delta),
WRAP_FUNC)
Return Wrap_func
Return Wrap_loop
Task function
@sync_loop_call (delta=10 * 1000)
def worker ():
"""
Do something
"""
Add a task
if __name__ = = "__main__":
Worker ()
App.listen (Options.port)
Tornado.ioloop.IOLoop.instance (). Start ()
After doing this, when the Web application is started, the scheduled task will run with it, and because it is event-based and asynchronous, it does not affect the normal operation of the Web service, and the task must not be blocked or computationally intensive. I'm mainly crawling data here, and using Tornado's own asynchronous fetch method.
In the Sync_loop_call decorator, I added a @gen.coroutine adorner on the Wrap_func function, which ensures that only the Yeild function is executed before the add_timeout operation is performed. If there is no @gen.coroutine adorner. Then the add_timeout will be executed without waiting for Yeild to return.
A complete example can be found on my GitHub, a project built on Heroku. Used to show GitHub user activity rankings and user area distributions. You can access the Github-data view. Because the domestic heroku by the wall, need to turn over the wall to access.
Summarize
Tornado is a non-blocking Web server as well as a web framework, but only using an asynchronous library in use can really play its asynchronous advantage, sometimes because the app itself is not very demanding, and if it is not blocking is particularly serious, there is no problem. In addition to using the Coroutine module for asynchronous programming, when a function is encapsulated in a function, in the function, even if there is an error, if not to capture the words will not be thrown, which is very difficult to debug.