Getting started with Tornado framework

Getting started with Tornado framework

pip install tornado

Manual installation
Download the installation package tornado-4.3 tar. gz( https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz)

$ tar xvzf tornado-4.3.tar.gz
$ cd tornado-4.3
$ python setup.py build
$ sudo python setup.py install

Write a hello first py

import tornado.ioloop
import tornado.web
 
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")
 
def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])
 
if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

Execute Python hello Py, open the browser to access http://localhost:8888/ You can see the normal output of the server Hello, world.

An ordinary tornado web server usually consists of four components:

  • The ioloop instance, which is a global tornado event loop, is the core engine of the server. In the example, tornado ioloop. IOLoop. Current () is the default tornado ioloop instance.

  • App instance, which represents a completed back-end app. It will be connected to a server socket port to provide external services. There can be multiple app instances in an ioloop instance. There is only one app instance in the example. In fact, multiple app instances can be allowed, but they are rarely used.

  • handler class, which represents business logic. When developing the server, it is to write a pile of handlers to serve the client requests.

  • Routing table, which connects the specified url rule with the handler to form a routing mapping table. When the request arrives, query the routing mapping table according to the requested access url to find the corresponding service handler.

The relationship between these four components:
An ioloop contains multiple apps (managing multiple service ports), an app contains a routing table, and a routing table contains multiple handlers. Ioloop is the core of the service engine. It is the engine, which is responsible for receiving and responding to client requests, driving the operation of business handlers, and executing scheduled tasks within the server.
When a request arrives, ioloop reads the request and unpacks it into an http request object, finds the routing table of the corresponding app on the socket, queries the handler attached in the routing table through the url of the request object, and then executes the handler. After the handler method is executed, it will generally return an object. Ioloop is responsible for packaging the object into an http response object and serializing it to the client.

Note: the same ioloop instance runs in a single threaded environment.

Factorial service

Write a normal web server that will provide factorial services. That is to calculate n! Value of. The server will provide factorial cache, and the calculated ones will be saved. There is no need to recalculate next time. The advantage of using Python is that you don't have to be careful that the calculation result of factorial will overflow. The integer of Python can be infinite.

# fact.py
import tornado.ioloop
import tornado.web

class FactorialService(object):  # Define a factorial service object
    def __init__(self):
        self.cache = {}   # Use a dictionary to record the factorials that have been calculated
 
    def calc(self, n):
        if n in self.cache:  # If there is a direct return
            return self.cache[n]
        s = 1
        for i in range(1, n):
            s *= i
        self.cache[n] = s  # Cache
        return s
 
class FactorialHandler(tornado.web.RequestHandler):
    service = FactorialService()  # new out factorial service object
 
    def get(self):
        n = int(self.get_argument("n"))  # Get the parameter value of url
        self.write(str(self.service.calc(n)))  # Using factorial services

def make_app():
    return tornado.web.Application([
        (r"/fact", FactorialHandler),  # Register routing
    ])
 
if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

Execute Python fact Py, open the browser and type http://localhost:8888/fact?n=50 , you can see the browser output
6082818640342675608722521633212953768875528313792102400000000. If the n parameter is not provided, visit http://localhost:8888/fact , you can see that the browser outputs 400: Bad Request. The request is wrong because no parameters are provided.

Using Redis

The above example is to store the cache in the local memory. If you change a port and use a factorial service to access it through this new port, it needs to be recalculated for each n, because the local memory cannot be shared across processes and machines.

Therefore, in this example, Redis will be used to cache the calculation results, so that repeated calculation can be completely avoided. In addition, instead of returning plain text, a json will be returned, and a field name will be added in the response to say whether this calculation comes from the cache or the fact. In addition, the default parameter is provided. If the client does not provide N, it defaults to n=1.

import json
import redis
import tornado.ioloop
import tornado.web

class FactorialService(object):
    def __init__(self):
        self.cache = redis.StrictRedis("localhost", 6379)  # The cache has been changed to redis
        self.key = "factorials"
 
    def calc(self, n):
        s = self.cache.hget(self.key, str(n))  # Save the calculation results with hash structure
        if s:
            return int(s), True
        s = 1
        for i in range(1, n):
            s *= i
        self.cache.hset(self.key, str(n), str(s))  # Save results
        return s, False

class FactorialHandler(tornado.web.RequestHandler):
    service = FactorialService()
 
    def get(self):
        n = int(self.get_argument("n") or 1)  # Parameter defaults
        fact, cached = self.service.calc(n)
        result = {
            "n": n,
            "fact": fact,
            "cached": cached
        }
        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(json.dumps(result))

def make_app():
    return tornado.web.Application([
        (r"/fact", FactorialHandler),
    ])
 
if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

First, you need to start redis, open a cmd window, and use the cd command to switch the directory to the installation directory of redis (e.g. D:\redis) to run redis server exe redis. windows. conf.
When visiting again http://localhost:8888/fact?n=50 , you can see the browser output as follows
{"cached": false, "fact": 608281864034267560872252163321295376887552831379210240000000000, "n": 50}
, refresh again. The browser outputs {"cached": true, "fact": 6082818640342675608722521633212953768875528313792102400000000, "n": 50}. You can see that the cached field has changed from true to false, indicating that the cache has indeed saved the calculation results. Restart the process,
Visit this connection again and observe the browser output. You can find that the cached result is still true. This indicates that the cache result is no longer stored in local memory.

PI calculation service

Now the service provides a parameter n as the accuracy index of PI. The larger n is, the more accurate the PI calculation is. Similarly, the calculation results are cached in the Redis server to avoid repeated calculation.

# pi.py
import json
import math
import redis
import tornado.ioloop
import tornado.web
 
class FactorialService(object):
    def __init__(self, cache):
        self.cache = cache
        self.key = "factorials"
 
    def calc(self, n):
        s = self.cache.hget(self.key, str(n))
        if s:
            return int(s), True
        s = 1
        for i in range(1, n):
            s *= i
        self.cache.hset(self.key, str(n), str(s))
        return s, False
 
class PiService(object):
    def __init__(self, cache):
        self.cache = cache
        self.key = "pis"
 
    def calc(self, n):
        s = self.cache.hget(self.key, str(n))
        if s:
            return float(s), True
        s = 0.0
        for i in range(n):
            s += 1.0/(2*i+1)/(2*i+1)
        s = math.sqrt(s*8)
        self.cache.hset(self.key, str(n), str(s))
        return s, False
 
class FactorialHandler(tornado.web.RequestHandler):
    def initialize(self, factorial):
        self.factorial = factorial
 
    def get(self):
        n = int(self.get_argument("n") or 1)
        fact, cached = self.factorial.calc(n)
        result = {
            "n": n,
            "fact": fact,
            "cached": cached
        }
        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(json.dumps(result))
 
class PiHandler(tornado.web.RequestHandler):
    def initialize(self, pi):
        self.pi = pi
 
    def get(self):
        n = int(self.get_argument("n") or 1)
        pi, cached = self.pi.calc(n)
        result = {
            "n": n,
            "pi": pi,
            "cached": cached
        }
        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(json.dumps(result))
 
def make_app():
    cache = redis.StrictRedis("localhost", 6379)
    factorial = FactorialService(cache)
    pi = PiService(cache)
    return tornado.web.Application([
        (r"/fact", FactorialHandler, {"factorial": factorial}),
        (r"/pi", PiHandler, {"pi": pi}),
    ])
 
if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

Because both handlers need to use redis, redis is extracted separately and passed in through parameters. In addition, the Handler can pass parameters through the initialize function. When registering the route, it can pass any parameters by providing a dictionary. The key of the dictionary should correspond to the parameter name. Run Python PI Py, open the browser to access http://localhost:8888/pi?n=200 , you can see the browser output {"cached": false, "pi": 3.1412743276, "n": 1000}, which is very close to the PI.

Tags: Tornado

Posted by gotry on Sat, 16 Apr 2022 22:14:36 +0930