再次一头扎进Tornado的风暴眼中のイチ
认识Tornado
其实没有啥可说的,和Django一样是一个python web框架,但是轻量化、异步。后来由Facebook收购然后开源出去。
啥?Django是啥?和Tornado一样是一个python web框架,大而全。
Tornado是啥?。。。。。。。
。。。。。。
。。。。。。
。。。。。。
Django大而全,开发敏捷。
Tornado小而精,性能优越。
性能优越的原理
这个epoll啊,然后linux注册,有空闲时间,使用yeild,epoll通知,处理等等等等。。。。
原理复杂的我不想讲(其实还是讲不清楚)。。。。
安装
pip,pip,pip啊少年,我还能说啥
- server.py
# tornado的基础web框架模块
import tornado.web
import tornado.ioloop
- ``
- ``
# 首页处理类定义
# RequestHandler请求处理基础类,处理所有请求的信息和方法
class IndexHandler(tornado.web.RequestHandler):
# get方式请求
# 如果没有定义的请求方法,比如此时是POST方式访问,则或返回405: Method Not Allowed
def get(self):
# 返回响应信息的一个方法,Tornado会返回所规定的信息
self.write("Hello RedQueen!")
- ``
- ``
if __name__ == "__main__":
# Application是Tornado Web框架的核心应用类,与服务器对接的接口,
# 其中保存路由表,初始化的第一个参数就是路由信息元组/列表
app = tornado.web.Application([
(r"/", IndexHandler),
])
# listen方法绑定8000端口创建一个http服务器实例
# 此时服务器还未开始监听端口
app.listen(8000)
# 提示信息
print("请访问首页网址http://127.0.0.1:8000")
# ioloop是tornado的核心io循环模块,封装了Linux的epoll和BSD的kqueue,tornado高性能的基石。
# current返回当前线程的IOLoop实例
# start启动IOLoop实例的io循环,此时服务器开始监听端口
tornado.ioloop.IOLoop.current().start()
使用python执行文件就可以了
python server.py
之后你会发现程序在打印完提示信息后就停住了,这是你打开浏览器访问提示的网址就会发现神奇的一幕(其实也没啥神奇的,只是说明Tornado开始工作了。)
HTTP服务器
tornado有自己的HTTP服务器实现方法的,这个实现方法不仅能帮助我们理解tornado的启动方式而且有更加丰富的启动选项。
import tornado.web
import tornado.ioloop
# tornado 的 http模块
import tornado.httpserver
- ``
- ``
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello RedQueen!")
- ``
- ``
if __name__ == "__main__":
app = tornado.web.Application([
(r"/", IndexHandler),
])
# 创建HTTP服务器实例
# 接受应用对象app注册路由映射表
http_server = tornado.httpserver.HTTPServer(app)
# 将端口监听语句从app.listen(8000)修改为以下语句
# 同样的,此时有端口绑定,当时并没有监听
http_server.listen(8000)
# 提示信息
print("请访问首页网址http://127.0.0.1:8000")
tornado.ioloop.IOLoop.current().start()
多进程
注意,这里说的是多进程的实现,并不是多线程。
之前的程序实现的都是单进程的实例,如果我们需要用到多进程的实例怎么办呢?
import tornado.web
import tornado.ioloop
# tornado 的 http模块
import tornado.httpserver
- ``
- ``
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello RedQueen!")
- ``
- ``
if __name__ == "__main__":
app = tornado.web.Application([
(r"/", IndexHandler),
])
http_server = tornado.httpserver.HTTPServer(app)
# 多进程,使用bind进行端口和ip的绑定
# app.listen()只能使用在单进程的实例中
http_server.bind(8000)
# start方法指定开启几个进程,输入参数默认值为1,仅开启一个进程
# 如果为None或者小于等于0,则自动根据机器硬件的cpu核芯数创建同等数目的子进程
# 如果大于0,则创建与输入参数相同个数的子进程。
http_server.start(0)
# 提示信息
print("请访问首页网址http://127.0.0.1:8000")
tornado.ioloop.IOLoop.current().start()
另外:虽然tornado提供了这种开启多进程的方式但是因为:
- 每个子进程会复制一份IOLoop实例, 如果在创建子进程前IOLoop实例被改变,会影响到每一个子进程。
- 所有进程一次性开启,没有办法不停实现不停服务更新
- 所有进程共享一个端口,单独监控每一个进程变得困难
以上原因,所以建议手动开启多个进程,绑定不同的端口。
(什么?你问怎么手动开启?传入不同的端口号,将脚本执行n次就开启了n个进程了,并且端口号不同。)
命令行参数设置
之前的程序都是将参数写死,能不能灵活设置呢?当然是可以的。
tornado.options模块,全局定义、存储参数。
- tornado.options.define()用来定义options选项变量的方法,定义的变量可以在全局的tornado.options.options中获取使用,传入参数:
- name 选项变量名,须保证全局唯一性,否则会报“Option ‘xxx’ already defined in …”的错误;
- default 选项变量的默认值,如不传默认为None;
- type 选项变量的类型,从命令行或配置文件导入参数的时候tornado会根据这个类型转换输入的值,转换不成功时会报错,可以是str、float、int、datetime、timedelta中的某个,若未设置则根据default的值自动推断,若default也未设置,那么不再进行转换。可以通过利用设置type类型字段来过滤不正确的输入。
- multiple 选项变量的值是否可以为多个,布尔类型,默认值为False,如果multiple为True,那么设置选项变量时值与值之间用英文逗号分隔,而选项变量则是一个list列表(若默认值和输入均未设置,则为空列表[])。
- help 选项变量的帮助提示信息,在命令行启动tornado时,通过加入命令行参数 —help 可以查看所有选项变量的信息(注意,代码中需要加入tornado.options.parse_command_line())。
- tornado.options.options
全局的options对象,所有定义的选项变量都会作为该对象的属性。 - tornado.options.parse_command_line()
转换命令行参数,并将转换后的值对应的设置到全局options对象相关属性上。
追加命令行参数的方式是server.py --option=value
修改程序:
import tornado.web
import tornado.ioloop
import tornado.httpserver
# options模块
import tornado.options
- ``
# 定义服务器监听端口
tornado.options.define("port", default=8000, type=int, help="run server on the given port")
# 多值情况演示
tornado.options.define("values", default=[], type=str, multiple=True, help="test values")
- ``
- ``
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello RedQueen!")
- ``
- ``
if __name__ == "__main__":
app = tornado.web.Application([
(r"/", IndexHandler),
])
# 解析命令行参数
tornado.options.parse_command_line()
# 输出演示的多值情况
print(tornado.options.options.values)
http_server = tornado.httpserver.HTTPServer(app)
# 监听定义的端口
http_server.listen(tornado.options.options.port)
# 提示信息
print("请访问首页网址http://127.0.0.1:%s" % tornado.options.options.port)
tornado.ioloop.IOLoop.current().start()
执行以下命令就能看到现象了:
python server.py --port=9000 --values=python,c++,java,php,ios
可以看到端口开在了9000端口上,而且console界面也输出了values的值
配置文件参数设置
命令行有的时候也不方便,那可以定义配置文吗?当然也是可以的。
我们在程序的同级目录下新建文件config
port = 8000
values = ["python", "c++", "java", "php", "ios"]
你可能也发现了,配置文件的语法格式和Python的语法格式相同,事实上就是如此。
修改启动程序:
import tornado.web
import tornado.ioloop
import tornado.httpserver
# options模块
import tornado.options
- ``
# 定义服务器监听端口
tornado.options.define("port", default=8000, type=int, help="run server on the given port")
# 多值情况演示
tornado.options.define("values", default=[], type=str, multiple=True, help="test values")
- ``
- ``
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello RedQueen!")
- ``
- ``
if __name__ == "__main__":
app = tornado.web.Application([
(r"/", IndexHandler),
])
# 从配置文解析参数
tornado.options.parse_config_file("./config")
# 输出演示的多值情况
print(tornado.options.options.values)
http_server = tornado.httpserver.HTTPServer(app)
# 监听定义的端口
http_server.listen(tornado.options.options.port)
# 提示信息
print("请访问首页网址http://127.0.0.1:%s" % tornado.options.options.port)
tornado.ioloop.IOLoop.current().start()
然后直接执行server.py文件就可以了,可以看到执行的程序参数就是定义的配置文参数。
日志说明
当我们在代码中调用parse_command_line()或者parse_config_file()的方法时,tornado会默认配置标准logging模块,默认开启日志功能,并向标准输出(屏幕)打印日志信息。
如果想关闭tornado默认的日志功能,可以在命令行中添加—logging=none 或者在代码中执行如下操作:
from tornado.options import options, parse_command_line
options.logging = None
-
配置文件导入配置
由于使用prase_config_file()的时候,还需要使用tornado.options.define()来进行定义,而且不能使用字典格式。所以使用配置文件的时候,建议新建一个python文件(如config.py),然后在里面直接定义python类型的变量(可以是任何的python类型);在需要配置文件参数的地方,将config.py作为模块导入,并使用其中的变量参数。
比如我们新建配置文件config.py #!/usr/bin/env python
import os
- ``
# mariadb配置
database = {
'host': '127.0.0.1',
'port': 3306,
'name': 'redqueen',
'user': 'redqueen',
'password': 'password',
}
- ``
# Tornado app配置
settings = {
'template_path': os.path.join(os.path.dirname(__file__), 'templates'),
'static_path': os.path.join(os.path.dirname(__file__), 'statics'),
'cookie_secret': '8z+&w@86wfck8g0p%@nj8ixr731n!n$mkter4sm3sja6n6bc&1',
'xsrf_cookies': False,
'login_url': '/login',
'debug': True,
}
- ``
# 日志路径
log_path = os.path.join(os.path.dirname(__file__), 'logs/all.log')
修改启动程序如下:
import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
- ``
# 导入配置文件
import config
- ``
# 定义服务器监听端口
tornado.options.define("port", default=8000, type=int, help="run server on the given port")
- ``
- ``
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello RedQueen!")
- ``
- ``
if __name__ == "__main__":
# 将配置文件选项传入应用实例
app = tornado.web.Application([
(r"/", IndexHandler),
], **config.settings)
tornado.options.parse_command_line()
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(tornado.options.options.port)
# 提示信息
# python3.6 语法,注意版本,如果无法运行,更新方法就可以了
print(f"请访问首页网址http://127.0.0.1:{tornado.options.options.port}")
tornado.ioloop.IOLoop.current().start()