transferred from: http://m.blog.csdn.net/blog/joeyon/41956027first of all, the environment, python2.7,tornado3.0
Then this article discusses not how the tornado layer handles socket communication with Epoll, but how to process get/post requests asynchronously at the application level. Here is the text:
At the same time, apply @tornado.web.asynchronous and on the Get or POST method processing
@tornado. Gen.engineAdorners, which can be very convenient and asynchronous methods with callback parameters to implement non-blocking request processing. This is not the case for the official example, because the official example would have been to use an asynchronous HTTP client to pull the data and then go to the callback function execution, the code for the discussion HTTP client is not within the scope of this article. So I use a simple function to demonstrate, and this is more to illustrate the problem. Class
Registerhandler(Basehandler. Basehandler):
@tornado. Web.asynchronous
@tornado. Gen.engineDef
Get(
Self, *arg, **args): Response = yield Tornado.gen.Task (
Self. method_while) Print
"Response", response
Self. Finish () def
Method_while(
Self, *arg, **args): Callback (1)
This is what I wrote a request to process the Handler,get request to add the two adorners, the first adorner indicates that the GET function is not automatically cut off the output stream, you need to explicitly call the finish method. This decorator needs to be used with @tornado.gen.engine. The point is, to see @tornado.gen.engine's source def.
engine(func):
@functools. Wraps(func) def
wrapper(*args, **kwargs): Runner = None def
handle_exception(Typ, value, TB): if runner is not None: return runner.handle_exception (Typ, value, TB) return False with Exceptionstackcontext (handle_exception) as deactivate: Gen = func (*args, **kwargs) if isinstance (gen, types. Generatortype): Runner = runner (Gen, deactivate) Runner.run () &nBsp; return assert gen is none, gen deactivate () return wrapper (I deleted the original comment) We can look directly inside the WITH statement: Gen = func (*args, **kwargs) if isinstance (gen, types. Generatortype):
runner = runner (Gen, deactivate) Runner.run () return
Gen is our Get method, Gen=fun (*args,**kwargs) is equivalent to Gen=get (*args,**kwargs), and the Get method execution results are returned to Gen? No~no! The Get method has yield, which means that get (*args,**kwargs) returns the data of a generator type Generatortype, which is not executed immediately and needs to call his next or send method to execute to the next yield method. The specific usage of the generator and its send and next is beyond the scope of this article, please consult the official documentation. Can see it new a runner,gen passed in, and then execute the Run method, we follow the code of the Runner class's Run method: Def
Run(
Self): If
Self. Running or
Self. Finished: #判断是否是在运行中或者已经结束, if you are returning return try immediately:
Self. Running = True #让状态为运行中 while true: #进入循环 if
Self. Exc_info is None: #上次循环没有异常 try:if not
Self. Yield_point.is_ready (): #判断key是否可用 return next =
Self. Yield_point.get_result () #获取yield表达式的结果给next except Exception:
Self. Exc_info = Sys.exc_info () try:if
Self. Exc_info is not None:
Self. had_exception = True Exc_info =
Self. exc_info
Self. Exc_info = None yielded =
Self. Gen.throw (*exc_info) else:yielded =
Self. Gen.send (Next) #如果上次没异常, the Get function continues execution, where the task object is returned to yielded, which is equivalent to iterating over the task (the most up-to-date code snippet) and assigning the task to yielded each time it executes to yield Except stopiteration: #如果没有可以执行的了, back
Self. finished = True If
Self. Pending_callbacks and not
Self. Had_exception:raise Leakedcallbackerror (
"finished without waiting for callbacks%r"%
Self. pending_callbacks)
Self. Deactivate_stack_context ()
Self. Deactivate_stack_context = None return except Exception:
Self. finished = True Raise if isinstance (yielded, list): yielded = Multi ( yielded) if isinstance (yielded, yieldpoint): #如果yielded为Task类型 (Task inherits from Yieldpoint)
Self. Yield_point = yielded try:
Self. Yield_point.start (
Self) #执行task的start方法 except Exception:
Self. Exc_info = Sys.exc_info () Else:
Self. Exc_info = (Badyielderror (
"yielded unknown object%r"% yielded),) finally:
Self. running = False The specific steps can be seen in the comments in the above code, here we continue to see the Start method of the task: class
Task(Yieldpoint): (Omit other code) def
Start(
Self, runner):
Self. Runner = Runner
Self. Key = Object () Runner.register_callback (
Self. Key)
Self. kwargs[
"Callback"] = Runner.result_callback (
Self. Key)
Self. Func (*
Self. Args, * *
Self. Kwargs) (omit other code) here the Func method is the topmost method_while, passing a callback parameter in: Runner.result_callback (
Self. key), and then execute the method. In order to discuss the effect of this callback parameter, we assume that this parameter is not used in this method_while method, that is, the Callback:start method is returned after Method_while executes. Continue executing the second pass of that loop in the runner Run method, which will be directly in this line if not
Self. Yield_point.is_ready (): #判断key是否可用 return is returned directly, because the key corresponding to the value in results is none. Here are two nouns, where are results and key,result? is a field of runner, from the constructor of the runner below you can see that results is a dictionary: class
Runner(object): Def
__init__(
Self, Gen, Deactivate_stack_context): (Omit other code)
Self. Pending_callbacks = Set ()
Self. Results = {} (omitting other code) so how is the key set to results in this dictionary? Is in the Start method of the task, Runner.register_callback (
Self. key), the specific code can look at the above task class. This register_callback is a bit around, it sets the key to Pending_callbacks inside, this pending_callbacks is a set type, that is, a non-repeatable collection, The purpose of this set is to determine the existence of a key from this point each time, and key exists to indicate that there is a task to execute. In the above run method to determine whether key is available, is_ready each time will first determine whether the key is in this pending_callbacks, no direct report exception, some words will go to results to fetch results. We can see that results has always been empty ah, when to set the value of it?? Is the callback method that we did not execute in the method_while, namely Runner.result_callback (
Self. Key): def
Result_callback(
Self, key): def
Inner(*args, **kwargs): If Kwargs or Len (args) > 1:result = Arguments (args, Kwargs) el If Args:result = args[0] Else:result = None
Self. Set_result (key, result) return inner can be seen, he callback the actual invocation of the argument list is written into the results, return to the yield expression value response. So we find that it is important to execute the callback in the parameter in the custom method, write the data you want to return into the callback parameter, the value of the yield expression will be this parameter, and then the Run method proceeds to the next yield until the Get method is completed. Like the top example, the print result is: Response 1
Finally, what is the use of this asynchronous scheme? When it comes to asynchrony, newbies will certainly think it is Get/post method blocking is also OK, a GET request come over, get handler function if blocked, is bound to cause the entire server blocked! Do not think this is a relative web front-end request of the parallel asynchronous solution, single-threaded tornado after all is a single thread, method blocking, will inevitably lead to server blocking, no doubt. This asynchronous is relative to the GET request, When the Get method finishes without the adorner @tornado.web.asynchronous, the request is automatically broken, but after the adorner is added, the GET request can return directly and remain connected until the finish method is explicitly called to close the output stream. So the initiation of asynchronous (asynchronous: the matter to the person other than themselves to do, their own no matter) solution, get method directly return, to other functions to do, and then come back. Plus @tornado.gen.engine just put this idea down, the code is condensed to a minimum, this does not mean that the asynchronous processing function or the Get has a dead loop can also let the server processing other requests, you query the database to spend 10000s, then your server will certainly die 10000s , there are four solutions to this problem: CROUCHDB (with HTTP interface, asynchronous HttpClient call), optimizing current database, asynchronous HttpClient WebService, and thread. Recommend the second third, followed by the fourth, and finally the first (reason does not explain, seemingly crouchdb word of mouth is not good, I do not love, MONGO party floated). Even if it's an asynchronous httpclient, it's like this.
def handle_request (response): if Response.error: print "error:", Response.error else: print Response.bodyhttp_client = Asynchttpclient () http_client.fetch ("http://www.google.com/", Handle_request)
Also just after the fetch no matter how the socket pulls the data, the callback is also to be executed on this thread! Elsewhere, if there is a time-consuming operation or a dead loop, the data it pulls over is never in the Handle_request function.
Go Tornado Get/post request Asynchronous Processing framework Analysis