tornado学习
Hello, World
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()
执行python hello.py
,打开浏览器访问http://localhost:8888/就可以看到服务器的正常输出Hello, world
一个普通的tornado web服务器通常由四大组件组成。
- ioloop实例,它是全局的tornado事件循环,是服务器的引擎核心,示例中
tornado.ioloop.IOLoop.current()
就是默认的tornado ioloop实例。 - app实例,它代表着一个完成的后端app,它会挂接一个服务端套接字端口对外提供服务。一个ioloop实例里面可以有多个app实例,示例中只有1个,实际上可以允许多个,不过一般几乎不会使用多个。
- handler类,它代表着业务逻辑,我们进行服务端开发时就是编写一堆一堆的handler用来服务客户端请求。
- 路由表,它将指定的url规则和handler挂接起来,形成一个路由映射表。当请求到来时,根据请求的访问url查询路由映射表来找到相应的业务handler。
这四大组件的关系是,一个ioloop包含多个app(管理多个服务端口),一个app包含一个路由表,一个路由表包含多个handler。ioloop是服务的引擎核心,它是发动机,负责接收和响应客户端请求,负责驱动业务handler的运行,负责服务器内部定时任务的执行。
当一个请求到来时,ioloop读取这个请求解包成一个http请求对象,找到该套接字上对应app的路由表,通过请求对象的url查询路由表中挂接的handler,然后执行handler。handler方法执行后一般会返回一个对象,ioloop负责将对象包装成http响应对象序列化发送给客户端。
同一个ioloop实例运行在一个单线程环境下。
阶乘服务
下面我们编写一个正常的web服务器,它将提供阶乘服务。也就是帮我们计算n!
的值。服务器会提供阶乘的缓存,已经计算过的就存起来,下次就不用重新计算了。使用Python的好处就是,我们不用当心阶乘的计算结果会溢出,Python的整数可以无限大。
import tornado.ioloop
import tornado.web
class FactorialService(object): # 定义一个阶乘服务对象
def __init__(self):
self.cache = {} # 用字典记录已经计算过的阶乘
def calc(self, n):
if n in self.cache: # 如果有直接返回
return self.cache[n]
s = 1
for i in range(1, n):
s *= i
self.cache[n] = s # 缓存起来
return s
class FactorialHandler(tornado.web.RequestHandler):
service = FactorialService() # new出阶乘服务对象
def get(self):
n = int(self.get_argument("n")) # 获取url的参数值
self.write(str(self.service.calc(n))) # 使用阶乘服务
def make_app():
return tornado.web.Application([
(r"/fact", FactorialHandler), # 注册路由
])
if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
执行python fact.py
,打开浏览器,键入http://localhost:8888/fact?n=50,可以看到浏览器输出了
608281864034267560872252163321295376887552831379210240000000000
,如果我们不提供n参数,访问http://localhost:8888/fact
,可以看到浏览器输出了400: Bad Request
,告诉你请求错误,也就是参数少了一个。
-- 使用Redis
上面的例子是将缓存存在本地内存中,如果换一个端口再其一个阶乘服务,通过这个新端口去访问的话,对于每个n,它都需要重新计算一遍,因为本地内存是无法跨进程跨机器共享的。
所以这个例子,我们将使用Redis来缓存计算结果,这样就可以完全避免重复计算。另外我们将不在返回纯文本,而是返回一个json,同时在响应里增加字段来说名本次计算来源于缓存还是事实计算出来的。另外我们提供默认参数,如果客户端没有提供n,那就默认n=1。
import json
import redis
import tornado.ioloop
import tornado.web
class FactorialService(object):
def __init__(self):
self.cache = redis.StrictRedis("localhost", 6379) # 缓存换成redis了
self.key = "factorials"
def calc(self, n):
s = self.cache.hget(self.key, str(n)) # 用hash结构保存计算结果
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 FactorialHandler(tornado.web.RequestHandler):
service = FactorialService()
def get(self):
n = int(self.get_argument("n") or 1) # 参数默认值
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()
当我们再次访问http://localhost:8888/fact?n=3,可以看到浏览器输出如下{"cached": false, "fact": 2, "n": 3}
,再刷新一下,浏览器输出{"cached": true, "fact": 2, "n": 3}
,可以看到cached字段由true编程了false,表明缓存确实已经保存了计算的结果。我们重启一下进程,再次访问这个连接,观察浏览器输出,可以发现结果的cached依旧等于true。说明缓存结果不再是存在本地内存中了。
圆周率计算服务
接下来我们再增加一个服务,计算圆周率,圆周率的计算公式有很多种,我们用它最简单的。
我们在服务里提供一个参数n,作为圆周率的精度指标,n越大,圆周率计算越准确,同样我们也将计算结果缓存到Redis服务器中,避免重复计算。
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()
因为两个Handler都需要用到redis,所以我们将redis单独抽出来,通过参数传递进去。另外Handler可以通过initialize函数传递参数,在注册路由的时候提供一个字典就可以传递任意参数了,字典的key要和参数名称对应。我们运行python pi.py
,打开浏览器访问http://localhost:8888/pi?n=200
,可以看到浏览器输出{"cached": false, "pi": 3.1412743276, "n": 1000}
,这个值已经非常接近圆周率了。
原文:知乎钱文品大佬——https://zhuanlan.zhihu.com/p/37382503
以上。
原文地址:https://www.cnblogs.com/lovebkj/p/12994183.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 大数据下的质量体系建设
- PostgreSQL 日志系统 及 设置错误导致磁盘塞满案例
- 六、乘胜追击,将剩下的Git知识点搞定
- 树莓派基础实验39:解析无线电接收机PWM、SBUS信号
- nodejs源码分析第十九章 -- udp模块
- Spark Extracting,transforming,selecting features
- 逆向so文件调试工具ida基础知识点
- 二叉搜索树中的众数
- 了解递归:普通函数递归和非递归栈式实现之间的区别
- 字节真题 ZJ26-异或:使用字典树代替暴力破解降低时间复杂度
- curl命令半天没响应,有可能返回内容导致session挂了
- 查看JVM参数信息 查看G1堆的使用情况
- LC1263-AI寻路优化: 距离优先bfs -> heuristic + A* -> tarjan + A*
- 从Zookeeper 到 Elastic Job 的原理解析和使用(一)
- 从Zookeeper 到 Elastic Job 的Simple Job使用(二)