Tornado框架
"""
**主要特点**:
1、拥有异步非阻塞IO的处理方式
2、作为服务器,具有出色的抗负载能力
**使用场景**:
1、抢票、网页游戏、大量的http持久连接
"""
# tornado安装
pip install tornado
主要支持
# 功能支持
"""
1. url路由映射
2. request上下文管理
3. 基于模板的页面渲染
4. 异步I/O支持,超时处理
5. 服务器+客户端框架
6. 内部HTTP服务器
7. Websocket(HTML5)浏览器与服务器的双向实时通信
"""
概念
"""
同步:代码调用I/O时,需要等I/O执行完成后,才能返回调用给下次调用, 顺序执行
异步:代码调用I/O时,无需等I/O执行完,就可以将函数返回给下次调用使用
tornado框架客户端
同步访问HTTP的客户端 —— HTTPClient (需要函数执行完成后,才将函数返回)
异步访问HTTP的客户端 —— AsycHTTPClient (无需等待函数完成,即可把函数返回
协程
原理:一个线程在执行过程中,利用发送请求等待应答的时间,再执行其他函数、或请求
Tornado 主要是利用协程来实现的多并发。
异步: http.fetch("www.baidu.com", callback = self.on_response)
协程: response = yield http.fetch("www.badu.com")
self.write(response.body)
"""
Tornado框架的主体架构
Hellow world 样例代码
# Hellow World
# tornado 核心IO循环模块(封装 linux的epoll 和BSD的kqueue队列)
import tornado.ioloop
import tornado.web
# handele业务处理类
class MainHandler(tornado.web.RequestHandler):
# 处理get请求
def get(self):
self.write("Hello world")
def make_app():
# Application: 核心应用类,与服务器对应的接口,保存路由映射表
return tornado.web.Application([
(r"/", MainHandler),
])
def main():
# 实例化
app = make_app()
# listen 绑定监听端口,listen 方法用来创建 一个HTTP服务器实例
app.listen(8888)
# IOLoop.current() 返回当前线程的IOLoop实例
# IOLoop.start() 启动 IOLoop实例循环,开启监听
tornado.ioloop.IOLoop.current().start() # 不退出、处理用户请求
if __name__ == "__main__":
main()
创建服务器对象
# 方式 1 :
if__name__ == "__main__":
app = tornado.web.Application([
(r"/",IndexHandler)
])
app.listen(8000) # 创建一个服务器对象,并给服务器绑定一个端口,启动单进程
tornado.ioloop.IOLoop.current().start()
# 方式 2 :
if__name__ == "__main__":
app = tornado.web.Application([
(r"/",IndexHandler)
])
# 实例化一个HTTP服务器对象,将服务绑定在端口 8000
httpServer = tornado.httpserver.HTTPServer(app)
httpServer.listen(8000)
tornado.ioloop.IOLoop.current().start()
# 方式 3 :
# 启动多个线程
if__name__ == "__main__":
app = tornado.web.Application([
(r"/",IndexHandler)
])
# 实例化一个HTTP服务器对象
httpServer = tornado.httpserver.HTTPServer(app)
# 将服务器绑定到指定端口
httpServer.bin(8000)
# 启动多个进程
httpServer.start(3)
tornado.ioloop.IOLoop.current().start()
定义全局参数
"""
**tornado.optins模块**作用:
1、全局参数的定义、存储、转换
基础方法:
1、tornado.options.define() 定义 options 选项变量的方法
2、tornado.options.options() 使用 options 变量的方法
函数解析:
tornado.options.define(name,default = None,type = None,help = None,metavar = None,multiple = False,group = None,callback = None) # 定义options 选项变量的方法
- name # 变量名称,保持唯一性
- default # 设置选项的默认值,默认为None
- type # 设置变量类型,默认为 define的类型
- multiple # 设置选项变量是否可以是多个值,默认为false
- help # 选项变量的提示信息
**# 获取全局参数的方法**
1、tornado.options.parse_command_line() # 将命令行中的参数转化,并保存到 tornado.options.options
2、tornado.options.parse_config_file(path) # 从配置文件导入参数 path为文件路径
"""
# 1、命令行获取全局变量
import tornado.options
# 定义两个全局变量
tornado.options.define("port",default = 8000,type= int)
tornado.options.define("list",dafault = [],type = str)
if__name__ == "__main__":
# 通过命令行获取全局参数变量(手动启动程序时进行输入)
tornado.options.parse_command_line()
app = tornado.web.Application([
(r"/",IndexHandler)
])
httpServer = tornado.httpserver.HTTPServer(app)
# 使用全局变量 port
httpServer.bin(tornado.options.options.port)
httpServer.start(3)
tornado.ioloop.IOLoop.current().start()
# 2、从配置文件中获取全局变量
"""
/ config 配置文件中
port = 7000
list = ["one","two","three"]
"""
import tornado.options
# 定义两个全局变量
tornado.options.define("port",type= int)
tornado.options.define("list",type = str)
if__name__ == "__main__":
# 通过全局配置文件进行获取 config 文件
tornado.options.parse_config_file("config")
app = tornado.web.Application([
(r"/",IndexHandler)
])
httpServer = tornado.httpserver.HTTPServer(app)
# 使用全局变量 port
httpServer.bin(tornado.options.options.port)
httpServer.start(3)
tornado.ioloop.IOLoop.current().start()
# 3.目前常用方法(把config 当成一个包来用 ) config.py
"""
/ config.py 配置文件中
options = {
"port": 8000,
"list": ["one","two","three"]
}
"""
import tornado.options
import config
if__name__ == "__main__":
print(config.options.list)
app = tornado.web.Application([
(r"/",IndexHandler)
])
httpServer = tornado.httpserver.HTTPServer(app)
# 使用全局变量 port
httpServer.bin(config.options.port)
httpServer.start(3)
tornado.ioloop.IOLoop.current().start()
日志信息
1、使用parse_command_line() 或 parse_config_file(path) 时 tornado会默认开启日志 loggong模块
2、关闭日志:tornado.options.options.options.logging = None
项目基础框架
"""
views(包) / index.py 中
"""
import tornado.web
form tornado.web import RequestHandler
class IndexHandler(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
self.write("sunck is a good man")
"""
config.py 文件
"""
import os
BASE_DIRS = os.path.dirname(__file__)
# 参数
options = {
"port":9000
}
# 配置
settings = {
"debug":True,
"static_path":os.path.join(BASE_DIRS,"static") # 设置静态文件目录
"template_path" :os.path.join(BASE_DIRS,"templates") # 设置模板文件目录
}
"""
application.py 路由
建立一个tornado.web.Application 子类
"""
import tornado.web
# 从views文件中引入index文件、index中写handler函数
from views import index
import config
class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r"/",IndexHandler)
]
super(Application,self).__init__(handlers,**config.settings)
"""
server.py 主文件
"""
import tornado.web
import tornado.ioloop
import tornado.httpserver
import config
from application import Application
if__name__ == "__main__":
app = Application()
httpServer = tornado.httpserver.HTTPServer(app) # 创建服务器
httpServer.bind(config.options["port"]) # 将服务器绑定到指定的端口上
httpServer.start(5) # 启动五个进程,默认开启 1 个进程
tornado.ioloop.Ioloop.current().start()
路由解析
# 1、普通字符串固定匹配
class Application(tornado.web.Application):
def __init__(self):
Handlers = [
(r"/", index.MainHandler), # 匹配根路径
(r"/entry", index.EntryHandler), # 匹配/entry
(r"/entry/2015", index.Entry2015Handler), # 匹配/entry/2015
tornado.web.url(r"/",index.IndexHandler,{"world":"name"}, name = "index") # 传参
]
super(Application,self).__init__(handlers,**config.settings)
# 2、带参数的字符串路径,参数部分用()标识
# url handler
http://127.0.0.1:8000/entry/good/nice/handsome
http://127.0.0.1:8000/entry?a=1&b=2&c=3 # get 方式传递参数
RequestHandler.get_query_arguments(name) # 获取get 方法传递参数
# get传参接收实例
class EntryHandler(RequestHandler);
def get(self,*args,**kwargs): # 默认参数
a = self.get_query_argument("a")
pass
Handlers = [
(r"/entry"([^/+]), EntryHandler), ]
(r'entry'/(\w+)/(\w+)\(\w+),EntryHandler) # 用下面方式接收
(r'entry'/(?P<p1>\w+)/(?P<p2>\w+)\(?P<p3>\w+),EntryHandler) # p1、p2、p3 为名字
class EntryHandler(RequestHandler):
def get(self,h1,h2,h3,*args,**kwargs)
# 3、带默认值的参数路径
Handlers = [(r"/entry"([^/+]), EntryHandler), ]
class EntryHandler(tornado.web.RequestHandler);
def get(self,slug = 'default'): # 默认参数
pass
# 4、多参数路径
Handlers = [(r"/(\d{4}/\d{2}/\d{2}), DetailHandler), ]
class EntryHandler(tornado.web.RequestHandler);
def get(self,year,month,day,slug): # 默认参数
pass
# 5、传递参数
Handlers = [
(r"/entry", EntryHandler,{"word1":"good","word2":"nice"}), # 传入参数
]
class ProfileHandler(RequestHandler):
# 接收参数 函数初始化,应该和 django 中的__init__ 类似,一般用于为函数赋予初始属性
def initalize(self,database):
self.database = database
def get(self,*args,**kwargs):
print(self.word1,self.word2)
# 6、静态匹配 / 例如百度主页 例如:baidu/index.html
# 放在所有路由的最下面 (StaticFileHandler) 在 /html中 根据名字进行查找,若找不到则用默认的html文件
Handlers = [
(r"/(.*)$",tornado.web.StaticFileHandler,{"path":os.path.join(config.BASE_DIRS,"static/html"),"default_filename":"index.html"})
]
前端自动转义
# tornado 默认是开启了自动转义的
# 关闭自动转义
1、raw {% raw str% } # str 是返回的模板原生代码 , raw只能关闭一行
2、{{str}} {% autoescape None %} {{str}} # 关闭当前文档的自定义转义
3、settings/ "autoescape" :None # 在 settings 中进行设置,关闭项目自动转义
4、escape() # 在关闭自动转义后,可以使用该方法对特定的变量进行转义
RequestHandler(接入点函数)
from tornad.web import RequestHandler
from tornad.web import Application
# 1、RequestHandler.initalize() 函数初始化,应该和 django 中的__init__ 类似,一般用于为函数赋予初始属性
class ProfileHandler(RequestHandler): # 基类 RequestHandler
def initalize(self,database): # **函数初始化 __init__ 接收参数用**
self.database = database
# 2、调用请求处理(get、post等)方法之前的资源初始化处理
RequestHandler.prepare() #调用请求处理(get、post等)方法之前的资源初始化处理,预处理方法(判断用户权限是否满足需求)
RequestHandler.on_finish() # 清理对象占用内存或关闭数据库连接等操作
RequestHandler.set_default_headers()
RequestHandler.write_error()
lass ProfileHandler(RequestHandler): # 基类 RequestHandler
def initalize(self,database): # **函数初始化 __init__ 接收参数用**
self.database = database
def prepar(self): # 预处理方法,在执行对应请求方法前调用,任何HTTP请求都会调用prepar函数
pass # 判断用户是否符合规则,若不符合则直接返回错误
def on_finish(self): # 请求结束后,进行调用,可以资源的清理释放,或日志处理
pass
# 3、HTTP Action 处理函数(请求方法)
RequestHandler.get(*args,**kwargs)
RequestHandler.post(*args,**kwargs)
RequestHandler.head(*args,**kwargs) # 类似get,响应没有具体内容,用户获取请求头
RequestHandler.delete(*args,**kwargs) # 请求服务器删除指定资源
RequestHandler.patch(*args,**kwargs) # 请求修改局部内容
RequestHandler.put(*args,**kwargs) # 从客户端向服务器发送指定内容
RequestHandler.options(*args,**kwargs) # 返回url支持的所有http方法
输入捕获(从请求中获取参数)
# get、post 方法都可以获取
RequestHandler.get_argument(name) # 获取参数单体
RequestHandler.get_arguments(name) # 获取参数的列表,有多个名字的参数
# get方法获取
RequestHandler.get_query_argument(name) # 获取get方法的单体
class ErrorHandle(RequestHandler):
def get(self,*args,**kwargs):
flag = self.get_query_argument("flag")
self.write("flag")
RequestHandler.get_query_arguments(name) # 获取get方法的列表
#post方法获取参数
RequestHandler.get_body_argument(name) # 获取post方法的单体
RequestHandler.get_body_arguments(name) # 获取post方法的列表
# 根据cookie 名称获取cookie值
RequestHandler.get_cookie(name,default = None)
# 获取HTTP请求的一切信息
RequestHandler.request # 返回 tornado.httputil.HTTPServerRequest
响应函数(请求处理完成后返回给客户端的)
RequestHandler.set_status(status_code,reason = None) # 设置HTTP Response 中的返回码,描述状态码
RequestHandler.set_header(name,value) # 键值对的方式设置HTTP Response中头参数,覆盖之前的Header
RequestHandler.add_header(name,value) # 键值对的方式添加HTTP Response中头参数,不盖之前的Header
RequestHandler.set_default_header(self) # 在HTTP响应处理前被调用,重写预先设置的headers
RequestHandler.write(chunk) # 将给定的块chunk作为HTTP Body 发送给客户端
RequestHandler.finish(chunk = None) # 通知Tornado Response的生成工作完成,chunk参数需要传递给客户端的HTTP body
RequestHandler.render(template_name,**kwargs) # 用给定的参数渲染模板,参数可以传递到模板文件中(个人感觉是最常用的返回函数)
RequestHandler.redirect(url,permanent = False,status = None) # 重定向
RequestHandler.send_error(500) # 抛出异常,去执行 self.write_error(self,status_code,**kwargs)
RequestHandler.clear() # 清空本次请求中的消息头、消息体
#设置cookie值
RequestHandler.set_cookie(name,value,domain = None,expires = None, path = "/",expires_day = None)
- name # cookie 名
- value # cookie值
- domain # 提交cookie时匹配的域名
- path # 提交cookie时匹配的路径
- expires # cookie 有效时间
- expires_day # 设置有效时间天数
get_cookie("cookie","未登录") # 获取cookie值
RequestHandler.clear_all_cookies(path="/",domain = None) #清空本次请求的cookies值
clear_cookie(name,path = "/",domain =None) # 删除名为name的cookie,并同时匹配domain和path的cookie
tornado.httputil.HTTPFile对象
# 作用:是接收到的文件对象
# 属性:
1、filename # 文件的实际名字
2、body # 文件的数据实体
3、content_type # 文件的类型
import os
import config
def post(self,*args,**kwargs):
filesDict = self.request.files
for inputname in filesDict:
fileArr = fileDict[inputname]
for fileObj in fileArr:
filePath = "服务器路径"
with open (filePath,"wb") as f:
f.write(fileObj.body)
files.filename # 文件名
files.body # 文件内容
files.content_type # 文件类型
模板部分
# 1、配置模板路径
# config.py
settings = {
"static_path":os.path.join(BASE_DIRS,"static") # 设置静态文件目录
"template_path" :os.path.join(BASE_DIRS,"templates") # 设置模板文件目录
"autoescape":None # 关闭项目字符转义 (一般不要关闭,不写即可)
}
# 2、渲染并返回给客户端
class HomeHandler(RequestHandler):
def get(self,*args,**kwargs):
temp = 100
per = {} # 多个参数
# 传递函数
def mySum(n1,n2):
return n1+n2
# 函数传递到模板中,模板中可以直接调用此函数
self.render('home.html',mySum = mySum,num = temp, per = per)
self.write(per) # 传输数据、可以直接将per数据转化成JSON格式
tornado与数据库的交互
"""
无自带的ORM,对数据库需要自己适配
且目前无有效的驱动
"""
# /config 数据库配置
mysql = {
"host":"10.10.10.10",
"user":"root",
"passwd":"sunck",
"dbName":"tset"
}
# 路由配置数据库 application.py
# SunckMySQL 为封装好的 查询驱动
self.db=SunckMySQL(config.mysql["host"],config.mysql["user"],config.mysql["passwd"],config.mysql["dbMame"])
class StudentsHandler(RequestHandler):
def get(self,*args,**kwargs):
stus = self.application.db.get_all_obj("select * from students")
self.render('students.html',stus = stus)
框架的异步化+协程化
实现方式1 : 回调函数实现异步
实现方式 2 : 协程的方式实现异步
回调函数实现异步 — 多线程
import time
import threading
# 耗时操作 —— handler 获取数据(数据库、其他服务、循环耗时)
# 步骤2 :longIo 接收回调函数(callback = finish)
def longIo(callback):
# 步骤 3.2 : 回调函数在 run执行完毕后进行执行,所以将 其在传给 run
def run(cb):
print("开始执行耗时操作")
time.sleep(5)
print("耗时操作结束")
# 耗时操作返回的数据无法直接返回给用户的调用函数,所以需要用到回调函数
# return data
cb ("sunck is good man")
# 此处建一个线程 进行去操作耗时操作,通过线程将 callback函数传给 run 函数
# 步骤 3.1
threading.Thread(target=run,args = (callback,)).start()
# 回调函数,data 为耗时操作返回的数据
def finish(data):
print("开始处理回调函数")
print("接收到 longIo的响应数据":data)
print("结束处理回调函数")
# 用户A的请求
def reqA():
print("开始处理A的请求")
# 步骤 1:
# longIo为耗时操作,将需要执行的回调函数finish传给 longIo
longIo(finish)
print("结束处理A的请求")
# 用户B的请求
def reqB():
print("开始处理B的请求")
print("结束处理B的请求")
# 主函数
def main():
reqA()
reqB()
whilt1:
time.sleep(0.5)
pass
if__name__=="__main__":
main()
协程实现异步
# 版本 1:
"""
基本过程描述 :
1、创建 reqA 生成器、通过next 进行执行reqA函数
2、当执行到 yield 时,调用了 longIo 函数,并将此函数挂起
2.1 在 reqA 中将通过 global gen 将 gen 定义为全局变量,以便 longIo 耗时 完成后调用进行返回
2.2 耗时执行完成后,通过需要通过gen.send() 进行将数据返回给 reqA进行唤醒
3、挂起后,另一方面继续向下执行,执行reqB 函数
4、当挂起的 longIo 函数有返回时,继续向下执行 reqA 函数
"""
# 用户A的请求
# 将gen定义为全局
gen = None
def reqA():
print("开始处理A的请求")
res = yield longIo()
print("接收到 longIo的响应数据":res)
print("结束处理A的请求")
# 用户B的请求
def reqB():
print("开始处理B的请求")
print("结束处理B的请求")
def longIo():
def run(cb):
print("开始执行耗时操作")
time.sleep(5)
try:
global gen
# 生成器gen.send 进行唤醒 reqA,将内容返回给reqA 函数的res接收, 但生成器目前在 longIo 函数中没有,所以需要定义为全局变量
gen.send("sunck is a good man")
except StopIteration as e:
pass
print("耗时操作结束")
threading.Thread(target=run).start()
# 主函数
def main():
# 创建reqA函数的生成器,通过next提取生成器中的值
# 定义global gen 定义全部,覆盖前面的 gen = None
global gen
gen = reqA()
next(gen)
reqB()
whilt1:
time.sleep(0.5)
pass
# 版本 2
"""
版本 1 中针对 reqA() 函数不能等同于普通的函数,需要将其协程一个生成器。为了使reqA等同于普通函数,所以产生版本 2,通过装饰器进行执行。
"""
# 手写生成器
def genCoroutine(func):
def wrapper(*args,**kwargs):
global gen
gen = func(*args,**kwargs)
next(gen)
return wrapper
# 给A 函数增加生成器,在执行 A函数 前执行生成器
@genCoroutine
def reqA():
print("开始处理A的请求")
res = yield longIo()
print("接收到 longIo的响应数据":res)
print("结束处理A的请求")
# 用户B的请求
def reqB():
print("开始处理B的请求")
print("结束处理B的请求")
# 耗时操作
def longIo():
def run(cb):
print("开始执行耗时操作")
time.sleep(5)
try:
global gen
# 生成器gen.send 进行唤醒 reqA,将内容返回给reqA 函数的res接收, 但生成器目前在 longIo 函数中没有,所以需要定义为全局变量
gen.send("sunck is a good man")
except StopIteration as e:
pass
print("耗时操作结束")
threading.Thread(target=run).start()
# 主函数
def main():
reqA()
reqB()
whilt1:
time.sleep(0.5)
pass
# 版本 3:
"""
版本 2 中 还存在一个一个全局变量 gen,所以需要解决变量的问题,并且 longIo 中还写了一个线程,所以产生了版本3 对这两处进行了改进
"""
# 手写生成器 —— 终极版本 以后不用写 、genCoroutine已经做了封装,直接调用即可
def genCoroutine(func):
def wrapper(*args,**kwargs):
gen1 = func() # reqA的生成器
gen2 = next(gen1) # longIo的生成器
def run(g):
res = next(g)
try:
gen1.send(res) # 返回给 reqA数据
except StopIteration as e:
pass
threading.Thread(target = run,args =(gen2,)).start()
return wrapper
# 给A 函数增加生成器,在执行 A函数 前执行生成器
@genCoroutine
def reqA():
print("开始处理A的请求")
res = yield longIo()
print("接收到 longIo的响应数据":res)
print("结束处理A的请求")
# 用户B的请求
def reqB():
print("开始处理B的请求")
print("结束处理B的请求")
# 耗时操作
def longIo():
print("开始执行耗时操作")
time.sleep(5)
print("耗时操作结束")
# 返回数据
yield "sunck is a good man"
# 主函数
def main():
reqA()
reqB()
whilt1:
time.sleep(0.5)
pass
handler客户端实例化—回调函数实现异步 — 装饰器
# 框架的异步化+协程化
# 1、异步化:针对RequestHandler 使用 @tornado.web.asynchronous 修饰器,长连接保持 + 将同步机制更改为异步机制(callback)。
# 2、协程化:针对RequestHandler 使用 @tornado.gen.coroutine 修饰器,将同步机制更改为协程异步机制
import tornado.ioloop
import tornado.web
import tornado.httpclient
class MainHandler(tornado.web.RequestHandler):
# 长连接、保持装饰器 + 异步机制装饰器
@tornado.web.asynchronous
def get(self):
# 将 handler 异步请求客户端实例化
http = tornado.httpclient.AsyncHTTPClient()
http.fetch("http://www.baidu.com",
callback=self.on_response)
def on_response(self, response):
if response.error: raise tornado.web.HTTPError(500)
self.write(response.body)
self.finish()
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])
def main():
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
if __name__ == "__main__":
main()
handler客户端实例化 — 协程实现异步 — 装饰器
# 框架的异步化+协程化
# 1、异步化:针对RequestHandler 使用 @tornado.web.asynchronous 修饰器,将同步机制更改为异步机制(callback)。
# 2、协程化:针对RequestHandler 使用 @tornado.gen.coroutine 修饰器,将同步机制更改为协程异步机制。
"""
tornado.httpclient.AsyncHTTPClient —— tornado 提供的异步web请求客户端,用来进行异步 web 请求
fetch(request,callback = None) —— 用于执行 web 请求,并异步响应返回一个tornado.httpclient.Httpclient.HttpResponse
- request 可以是 一个 URL 也可以是一个tornado.httpclient.HTTPRequest对象
**tornado.httpclient.HTTPRequest对象** HTTPRequest
1、http请求类,该类的构造函数可以接收参数
(1)、url —— 字符串,要访问的网址,必传
(2)、method —— 字符串类型 ,http 请求方法
(3)、headers —— 字典或HTTPHeaders 、附加协议头
(4)、body —— http 请求体
**tornado.httpclient.Httpclient.HttpResponse** HttpResponse
1、http响应的类
2、属性
(1)、code —— 状态码
(2)、reason —— 状态码描述
(3)、body —— 响应数据
(4)、error —— 异常
"""
import tornado.ioloop
import tornado.web
import tornado.httpclient
from tornado.httpclient import AsyncHTTPClient
class MainHandler(tornado.web.RequestHandler):
# 协程异步装饰器 (生成器 + 保持长连接)
@tornado.gen.coroutine
def get(self):
# 创建异步请求客户端
http = tornado.httpclient.AsyncHTTPClient()
# fetch 向 url发送请求
response = yield http.fetch("http://www.baidu.com")
# 若此处不用 协程 还可以用异步函数进行处理 http.fetch("http://www.baidu.com",self.on_response)
# on_response 为回调函数,用于处理 返回的数据,只要在写一个 on_response 函数即可处理。
self.write(response.body)
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])
def main():
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
if __name__ == "__main__":
main()
# 异步: http.fetch("www.baidu.com", callback = self.on_response)
# 协程:response = yield http.fetch("www.badu.com")
self.write(response.body)
handler作为客户端 — 回调异步 - 通信信道保持及关闭
# handler 同步客户端 HTTPClient
from tornado.httpclient import HTTPClient
def synchronous_visit():
http_client = HTTPClient()
response = http_client.fetch("www.baidu.com") # 访问百度,有返回界面后才会继续执行下面的
print response.body
# handler 异步客户端 AsycHTTPClient
from tornado.httpclient import AsycHTTPClient
def handle_respinse(response):
print response.body
self.finish() # 函数完成后关闭通信通道
@tornado.web.asynchronous # 不关闭通信的通道,为回调函数提供数据通道
def asynchronous_visit():
http_client = AsycHTTPClient()
# 将请求返回的数据 交给 handle_reponse 函数进行处理
http_client.fetch("www.baidu.com",callback = handle_reponse) # 访问百度后立刻返回,百度有反馈后asynchronous_visit会调用handle_response接收
# 本人理解是将原来的函数拆分成两个部分,一个部分取等待函数的响应,一分部返回,所以原函数可以继续使用
handler 作为客户端请求协程异步
class Students2Handler(RequestHandler):
@tornado.gen.coroutine # 协程异步装饰器
def get(self,*args,**kwargs):
url = "baidu.com.cn...."
# 创建异步客户端
client = AsycHTTPClient()
res = yield client.fetch(url)
if res.error:
self.send_error(500)
else:
data = json.loads(res.body)
self.write(data)
handler 作为客户端请求协程异步 - web请求独立
class Students3Handler(RequestHandler):
@tornado.gen.coroutine # 协程异步装饰器
def get(self,*args,**kwargs):
res = yield self.getData()
self.write(data)
@tornado.gen.coroutine
def getData(self):
url = "baidu.com.cn...."
# 创建异步客户端
client = AsycHTTPClient()
res = yield client.fetch(url)
if res.error:
ret = {"ret":0}
else:
data = json.loads(res.body)
raise tornado.gen.Return(ret)
协程函数的调用
"""
方法1:在本身协程的函数内通过yield关键字调用 (可以看上面的例子)
方法2:在IOLoop尚未启动时,通过IOLoop的 run_sync()函数调用
方法3:在IOLoop已经启动时,通过IOLoop的 spawn_callback()函数调用
"""
# **方法1:通过yield关键字调用**
# 因为两个函数都是协程函数,所以可以通过yield进行相互调用
from tornado import gen # 引入协程库 gen
@gen.coroutine # 协程声明
def outer_corouterine(): #协程函数一
print("第一行")
yield coroutine_visit() #协程函数二
print("第二行")
# **方法2:IOLoop开启,spawn_callback())函数调用**
from tornado.ioloop import IOLoop
def func_normal():
print("第一行")
IOLoop.current().run_sync(lambda:coroutine_visit()) #调用协程函数 coroutine_visit()
print("第二行")
# **方法3:IOLoop未开启,run_sync()函数调用**
#开启状态
from tornado.ioloop import IOLoop
def func_normal():
print("第一行")
IOLoop.current().spawn_callback(coroutine_visit) #调用协程函数 coroutine_visit()
print("第二行")
# spawn_callback() 只能调用没有返回值的协程函数
协程中利用阻塞函数(利用线程池)
from concurrent.futures import ThreadPOOLExeutor # 引入线程池
thead_pool = ThreadPOOLExeutor(2) # 线程池中含两个线程
@gen.coroutine # 协程声明
def call_blocking():
print("第一行")
yield thead_pool.submit( mysleep, 10) # mysleep 是被挂起的函数名,后面的是10 是函数中所需的参数
print("第二行")
协程中等待多个异步调用
# 通过yield 关键字 来等待多个异步调用,这些调用只要通过list 或字典的方式传给yield即可
from tornado import gen # 引入协程库 gen
from tornado.httpclient import AsycHTTPClient
@gen.coroutine # 协程声明
def coroutine_visit(): #协程函数一
http_client = AsycHTTPClient()
list_response = yield [http_client.fetch("www.baidu.com"), # 多个协程访问,返回后打印 response.body
http_client.fetch("www.163.com"),
http_client.fetch("www.xinlang.com"),
http_client.fetch("www.tengxun.com"),]
for response in list_response:
print response.body