Tornado学习笔记


web应用部署时,采用wsgi协议与服务器对接,这类通常是基于多线程的,也就是说每一个请求都会有一个线程来处理。这种不适用于以下两种场景

场景一:用户量大、高并发(双十一、抢火车票)

场景二:大量的HTTP持久连接,长期占用服务器资源,容易过负荷。

C10K问题

单台服务器无法处理10w+以上并发,而采用多台又会增加成本,tornado旨在解决该问题,它是一个高性能的解决方案(服务器与框架的集合体)

tornado是一个轻量级的web框架,拥有异步非阻塞的处理方式

Tornado和Django比较

tornado.ioloop

tornado充分利用linux的epoll工具和BSD的kqueue工具,是Tornado不依赖多进程/多线程而达到高性能的原因

epoll去管理socket(相当于socket的容器),应用程序IOloop向epoll发送请求,所以要不断循环(对应ioloop)询问epoll

每次循环请求epoll时,哪个socket有数据就对哪个处理,不需要分配资源监听不必要的socket;

tornado单线程循环(当完成一个socket的响应时,才向下执行下一个),当执行的方法中有耗时较长的程序时,就会发生阻塞,程序就会卡死;这时候就用到异步

传入参数

url拼接,获取请求头参数(get请求)

get_query_argument(name, default=DEFAULT, strip=True)
# strip 是否删除左右空白符
get_query_arguments(name, strip=True) # 传入多个参数,返回List

获取请求体参数(post请求)

get_body_argument(name, default=DEFAULT, strip=True)
get_body_arguments(name, strip=True)

说明

传入json数据

json_data = self.request.body # 获取json数据
dict_data = json.loads(json_data)

上传文件

img_data = self.request.files["key"]
self.write() # 将返回的人数据写到缓冲区中,等到方法执行完毕后,一起返回,所以会执行所有的self.write方法
self.finish() # 把缓冲区的内容写到socket中,结束本次请求
self.set_header(name,value) # 设置返回头
self.status(status_code,reason=None) # 设置状态码
self.redirect(url) # 跳转
self.send_error(status_code) #传递给write_error方法,返回浏览器错误页面,调用完send_error会自动调用finish,代码后再调用write会报错

python在进行字符串验证拼接时,默认会以ascii编码方式转换为unicode,但是ascii又不能编码中文,所以会报错

json格式数据实现不同语言之间的数据传输

RequestHandler的方法,和post,get方法同级
prepare # 访问该接口的任何http请求都会执行该方法,做预处理
on_finish # 请求结束,数据返回客户端后再执行一些清理操作
set_default_headers # 设置header头
write_error # 自定义错误页面

安全应用

1、cookie,存储在浏览器的键值对

self.set_cookie()
或者
self.set_header()

浏览器根据响应头中的set-cookie字段在本地设置

cookie存储在浏览器端很容易被篡改,为了防止恶意篡改,需要给cookie配置一个随机字符串作为密钥

设置xsrf_cookie

2、用户认证

@tornado.web.authenticated
def get(self):
    pass

认证装饰器调用get_current_user()方法,可以在该方法中自定义完成方法的认证逻辑

如果认证失败,跳转到登陆页,登录页的url需要自定义

如果多个线程之间是相互独立的,那多线程就可以称为异步

异步也是同时进行,那异步是否要求两个操作间互相独立?

使用yield关键字让函数变成一个生成器,调用该函数时,就是创建了一个生成器;

yield异步,再函数中开启一个新的线程,新的线程和主线程相互不影响,完成异步

tornado异步

tornado利用epoll实现异步,程序的挂起与唤醒始终在一个线程上,由tornado自己来调度,属于真正意义的协程

ioloop是跟epoll交互的接口

epoll是轮询socket,并询问socket状态,分配任务

把耗时长的handler通过yield交给ioloop,然后主程序去继续遍历socket

epoll主要是解决网络IO的并发问题,所以Tornado的异步编程也主要体现在网络IO的异步上,即异步web请求,

tornado提供了一个异步请求客户端

tornado.httpclient.AsyncHTTPClient

装饰器

# 回调的方式实现异步
@tornado.web.asynchronous #get执行完后不要关闭通道,等待耗时执行的结果,执行回调函数
def get(self):
    client = AsyncHTTPClient()
    client.fetch("http://www.baidu.com",callback=self.on_response)

def on_response(self, resp):
    data = json(resp.body)
    self.write(data.get("city",""))
    self.finish()

# 同步的方式实现异步
@tornado.gen.coroutine
def get(self):
    client = AsyncHTTPClient()
    resp = yield client.fetch("http://www.baidu.com")
    data = json(resp.body)
    self.write(data.get("city",""))
    self.finish()

对一次访问请求并不会提升速度,但是会提升多个人访问时候的并发能力

一个函数中出现出现了yield就不允许用return返回数据,tornado封装了用于在生成器中返回值的特殊异常

raise tornado.gen.Return(rep) # 异步返回数据

异步web请求

tornado提供了异步请求web客户端tornado.httpclient.AsyncHTTPClient

fetch(request,callback=None)

协程异步

经常使用的异步操作封装成函数

websocket

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端直接向客户端推送数据而不需要客户端进行请求,两者之间可以创建持久性的连接,并允许数据进行双向传递

模块tornado.websocket,其中提供了一个WebSocketHandler类来处理通讯

websocket建立连接也是被动的,等待客户端发送请求;但是可以主动向客户端发送消息和关闭连接

JS与WebSocket通讯

聊天室demo

部署Tornado

管理多进程

tornado配置文件