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.