Code example for asynchronous processing of Python Web framework Tornado

Source: Internet
Author: User

Code example for asynchronous processing of Python Web framework Tornado
1. What is Tornado

Tornado is a lightweight but high-performance Python web framework. Compared with another popular Python web framework Django, tornado does not provide ORM interfaces for database operations and strict MVC development modes, but it can provide basic web server functions, so it is lightweight. It uses the non-blocking and event-driven I/O model (epoll or kqueue) it implements a set of Asynchronous Network Libraries, so it is high-performance.

Tornado's lightweight and high-performance features make it especially suitable for providing web APIs. if used properly, its non-blocking and asynchronous capabilities can address the C10K problem.

Note that due to Python GIL, multithreading is always a "characteristic" of Single-core execution. When tornado processes http requests, if the backend response of a request is blocked (for example, reading data from the database or disk causes a long processing time), other http requests will also be blocked, this will seriously drag down tornado's performance in high concurrency scenarios.

Fortunately, tornado provides the ability to process requests asynchronously. In asynchronous mode, we can pass in callback functions or use tornado. gen. the coroutine modifier allows tornado's internal io loop to still accept other http requests while waiting for the response results of the current request. This avoids the impact of a time-consuming operation On tornado's processing capability.

2. How to Write asynchronous processing code under tornado framework

The Tornado official documentation provides several simple examples of asynchronous code, but to be honest, the code is too simple (the Basic asynchronous syntax is displayed in the get or post function of the handler class of a uri), which has little practical significance.

In actual projects, complicated processing logic cannot be heap in the get or post functions, but is encapsulated in other classes for the get or post function calls of the handler class. Therefore, this article provides a slightly complex example to illustrate how to Implement Asynchronous processing logic in functions of other classes to achieve asynchronous processing of http requests.

Assume that the current requirement is to use tornado to implement a web server and support the uri method named cityhotel. When the client accesses the uri through an http GET request, the web server accesses the city specified by the query parameter, another back-end api that requests to store hotel detailed data. After business processing, it returns all the hotels in the city of a chain hotel to the client.
Assume that the url format of the client GET request is http: // host/api/hotel/cityhotel? City = xxx
Assume that the backend api interface for storing hotel detailed data is: http: // pai_backend/getCityHotels? City = xxx

According to the above scenario, since the web server implemented by tornado receives a request from the client, it also needs to request basic data through another API interface, and the latter will block tornado before returning, in this scenario, tornado is best to asynchronously request the API that provides the basic data to avoid uncontrollable backend dragging tornado's response performance.

Based on the business requirements described above, the following code demonstrates how to process services asynchronously.

Module entry file (main. py ):

#!/bin/env pythonimport tornado.ioloopimport tornado.webimport tornado.genimport hotelcoreclass CityHotelHandler(tornado.web.RequestHandler):    @tornado.gen.coroutine    def get(self):        ## parse query params        params = {}        keys = ['city']        for key in keys:            value = self.get_query_argument(key)            params[key] = value        (status, rsp) = yield hotelcore.HotelApiHandler.get_city_hotel(params['city'])        if 200 == status:            self.set_header('content-type', 'application/json')            self.finish(rsp)        else:            self.set_status(404)            self.finish()def main():    app_inst = tornado.web.Application([        (r'/api/hotel/cityhotel', CityHotelHandler),    ], compress_response = True)    app_inst.listen(8218)    tornado.ioloop.IOLoop.current().start()if '__main__' == __name__:    main()

The module that processes the business logic is encapsulated in the javascore. py file. The Code is as follows:

#!/bin/env python#-*- encoding: utf-8 -*-import jsonfrom tornado import genfrom tornado import httpclientclass HotelApiHandler(object):    _cfg_dict = {        'api_host' : 'api.hotelbackend.com',    }    @classmethod    @gen.coroutine    def get_city_hotel(cls, city):        ret = yield cls._parallel_fetch_city_hotel(city)        raise gen.Return((200, ret))    @classmethod    @gen.coroutine    def _parallel_fetch_city_hotel(cls, city):        base_url = 'http://%s/v1/getCityHotel' % (cls._cfg_dict['api_host'])        ## hote type: 1=normal room; 2=deluxe room        hotel_type = {'normal': 1, 'deluxe': 2}        urls = []        for v in hotel_type.values():            api_url = '%s?city=%s&level=%s' % (base_url, city, v)            urls.append(api_url)        ## issue async http request        http_clt = httpclient.AsyncHTTPClient()        rsps_dict = yield dict(normal_room = http_clt.fetch(urls[0]), deluxe_room = http_clt.fetch(urls[1]))        city_hotel_info = cls._parse_city_hotel(rsps_dict, city)        ret = { }        if len(city_hotel_info):            ret['errno']  = 0            ret['errmsg'] = 'SUCCESS'            ret['data']   = city_hotel_info        else:            ret['errno']  = 1            ret['errmsg'] = 'Service Not Found at This City'            ret['data']   = ''        raise gen.Return(ret)    @classmethod    def _parse_city_hotel(cls, rsp_dict, city):        city_hotel_info = {}        for hotel_level, rsp in rsp_dict.items():            rsp_json = json.loads(rsp.body)            datas = rsp_json['data']            for city_id, city_detail in datas.items():                name = city_detail['name']                if city in name:                    city_hotel_info[hotel_level] = city_detail                    break        return city_hotel_info

Some additional instructions on the above Code:

To compile tornado asynchronous processing code, you must be familiar with the decorator @ gen provided by tornado for Python's decorator syntax and generator/yield syntax. coroutine indicates that the decorated function is an asynchronous processing function. Calling this function will not block the main thread of tornado being @ gen. in the function decorated by coroutine, the time-consuming function that needs to be asynchronously executed is called using yield. yield itself returns a generator, combined with @ gen. after coroutine, it returns a Future-type object defined by tornado. during the execution of the function called by yield, the process control is returned to the main thread, so even if the function requires a longer running time, the main thread of tornado can continue to process other requests in Python 2. in the syntax of Version x, generator does not allow returning the return value of the function. The raise gen provided by tornado must be used. return (ret) to achieve the returned goal, which is a comparison of tri The cky ure object returned by the method yield of cky can obtain the return value of the function called by yield by calling the body attribute. As long as the above several points are understood, @ gen is returned. coroutine and yield have syntax meanings in tornado asynchronous programming. Therefore, writing complicated asynchronous calling code is different from coding synchronous calling code that implements the same functions but tornado's overall performance cannot be guaranteed, the implementation difficulty almost does not exist.

Many of the above Code's syntax details are not expanded, and we hope the Implementation ideas can help people. Pai_^

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.