一、Hello world
import tornado.ioloop
import tornado.web
# 定义handler类,处理相关的业务逻辑
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello World!")
def make_app():
# 定义app实例,并注册路由表
return tornado.web.Application([
# 路由表
(r"/", MainHandler),
])
if __name__ == '__main__':
app = make_app()
# 监听8888端口
app.listen(8888)
# 定义ioloop实例,并启动服务
tornado.ioloop.IOLoop.current().start()
这是官方提供了Hello, World实例,执行python hello.py,打开浏览器访问http://localhost:8888/就可以看到服务器的正常输出Hello, world。
二、阶乘服务:
它将提供阶乘服务。也就是帮我们计算n!的值。服务器会提供阶乘的缓存,已经计算过的就存起来,下次就不用重新计算了。使用Python的好处就是,我们不用当心阶乘的计算结果会溢出,Python的整数可以无限大。
# 阶乘服务
import tornado.ioloop
import tornado.web
# 定义服务类
class FantorialService(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
# 定义Handler类(业务逻辑类)
class FactorialHandler(tornado.web.RequestHandler):
# new一个阶乘服务对象
service = FantorialService()
def get(self):
# 获取url的参数
n = int(self.get_argument("n"))
# 使用阶乘服务
self.write(str(self.service.calc(n)))
# 定义app实例,并注册路由表
def make_app():
# 定义app实例
return tornado.web.Application([
# 注册路由表
(r"/fact", FactorialHandler),
])
if __name__ == '__main__':
app = make_app()
app.listen(8888)
# 创建ioloop实例,并启动服务
tornado.ioloop.IOLoop.current().start()
上面的例子是将缓存存在本地内存中,如果换一个端口再其一个阶乘服务,通过这个新端口去访问的话,对于每个n,它都需要重新计算一遍,因为本地内存是无法跨进程跨机器共享的。
所以这个例子,我们将使用Redis来缓存计算结果,这样就可以完全避免重复计算。另外我们将不在返回纯文本,而是返回一个json,同时在响应里增加字段来说名本次计算来源于缓存还是事实计算出来的。另外我们提供默认参数,如果客户端没有提供n,那就默认n=1。
# 使用redis缓存阶乘的计算结果
import json
import redis
import tornado.ioloop
import tornado.web
# 定义服务类
class FactRedis(object):
def __init__(self):
self.cache = redis.StrictRedis("localhost", 6379)
self.key = "factredis"
def calc(self, n):
# 用hash呃机构保存计算结果
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
# 定义业务逻辑类MainHandler
class FactRedisHandler(tornado.web.RequestHandler):
# 定义服务实例
service = FactRedis()
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))
# 定义app实例,并注册路由表
def make_app():
return tornado.web.Application([
(r"/fact", FactRedisHandler),
])
if __name__ == '__main__':
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
当我们再次访问http://localhost:8888/fact?n=50,可以看到浏览器输出如下
{"cached": false, "fact": 608281864034267560872252163321295376887552831379210240000000000, "n": 50}
,再刷新一下,浏览器输出{"cached": true, "fact": 608281864034267560872252163321295376887552831379210240000000000, "n": 50}
,可以看到cached字段由true编程了false,表明缓存确实已经保存了计算的结果。我们重启一下进程,
再次访问这个连接,观察浏览器输出,可以发现结果的cached依旧等于true。说明缓存结果不再是存在本地内存中了。
当我们使用http://localhost:8888/fact?n= 访问时,回使用1作为默认n值。
三、圆周率服务
# 计算圆周率服务
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.dums(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))
# 定义app示例,并注册路由
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}
,这个值已经非常接近圆周率了。