Tornado-基本入门
初识Tornado
什么是Tornado
全称Tornado Web Server,是一种Web服务器软件的开源版本。
特点
- 作为Web框架,是一个轻量级的Web框架,类似于另一个Python web框架Web.py,其拥有异步非阻塞IO的处理方式
- 作为Web服务器,Tornado有较为出色的抗负载能力,官方用nginx反向代理的方式部Tornado和其它Python web应用框架进行对比,结果最大浏览量超过第二名近40%
使用场景
- 用户量大,高并发
- 大量大HTTP持久连接
- 使用同一个TCP连接来发送和接收多个HTTP请求/应答,而不是为每一个新的请求/应答打开新的连接的方法
- 对于HTTP 1.0,可以在请求的包头(Header)中添加Connection:Keep-Alive=
- 对于HITP 1.1,所有的连接默认都是持久连接
C10K
上面的高并发问题,通常用C10K这一概念来描述。C10K——Concurrentlyhandling ten thousand connections,即并发10000个连接。对于单台服务器而言,根本无法承担,而采用多台服务器分布式又意味着高昂的成本
性能
Tornado在设计之初就考虑到了性能因素,旨在解决C10K问题,这样的设计使得其成为一个拥有非常高性能的解决方案(服务器与框架的集合体)
Tornado与Django对比
Django
- Django是走大而全的方向,注重的是高效开发,它最出名的是其全自动化的管理后台:只需要使用起ORM,做简单的对象定义,它就能自动生成数据库结构、以及全功能的管理后台
- Django提供的方便,也意味着Django内置的ORM跟框架内的其他模块耦合程度高,应用程序必须使用Django内置的ORM,否则就不能享受到框架内提供的种种基于其ORM的便利
- 特点
- session功能
- 后台管理
- ORM
Tornado
- Tornado走的是少而精的方向,注重的是性能优越,它最出名的是异步非阻塞的设计方式
- 特点
- HTTP服务器
- 异步编程
- WebSockets
安装Tornado
-
安装
1
pip install tornado==5.1.1
-
测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27# tornado的基础web框架
import tornado.web
# tornado的核心IO循环模块,封装了Linux的epoll和BSD的kqueue,是tornado高效的基础
import tornado.ioloop
# 类比Django中的视图,一个业务处理类
class IndexHandler(tornado.web.RequestHandler):
# 处理get请求的,不能处理post请求
def get(self, *args, **kwargs):
# 对应http请求的方法,给浏览器响应信息
self.write('sunck is a good man!')
if __name__ == '__main__':
# 实例化一个app对象
# Application:是tornado web框架的核心应用类,是与服务器的接口
# 里面保存了路由映射表,有一个listen方法用来创建一个http服务器的实例,并绑定了端口
app = tornado.web.Application([
(r'/', IndexHandler)
])
# 绑定监听端口
# 注意:此时服务器并没有开启监听
app.listen(8000)
# IOLoop.current():返回当前线程的IOloop实例
# IOLoop.start():启动IOLoop实例的I/O循环,同时开启了监听
tornado.ioloop.IOLoop.current().start() -
说明
- Tornado应该运行在类Unix平台,在线上部署时为了最佳的性能和扩展性,仅推荐Linux和BSD(因为充分利用Linux的epoll工具和BSD的kqueue工具,是Tornado不依靠多进程/多线程而达到高性能的原因)。
- 对于Mac OS x,虽然也是衍生自BSD并且支持kqueue,但是其网络性能通常不太给力,因此仅推荐用于开发
- 对于Windows,Tornado官方没有提供配置支持,但是也可以运行起来,不过仅推荐在开发中使用
Tornado高效的原理
httpserver
httpserver
对象
1 | import tornado.web |
单进程与多进程
-
⚠️注意:
tornado
服务默认启动的是单进程 -
开启多个进程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import tornado.web
import tornado.ioloop
# 引入httpserver模块
import tornado.httpserver
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.write('sunck is a good man!')
if __name__ == '__main__':
app = tornado.web.Application([
(r'/', IndexHandler)
])
httpServer = tornado.httpserver.HTTPServer(app)
# httpServer.listen(8000)
httpServer.bind(8000)
httpServer.start(4)
tornado.ioloop.IOLoop.current().start() -
说明
httpServer.bind(port)
:将服务器绑定到指定的的端口httpServer.start(num)
:- 默认开启一个进程;
- 值大于0,创建对应个数子进程
- 值为None或者小于等于0,开启对应机器硬件的CPU核心数个子进程
补充说明
app.listen(port)
:只能在单进程模式中使用- 多进程
- 虽然tornado给我们提供了一次性启动多个进程的方式,但是由于一些问题,不建议使用上面方式启动多进程,而是手动启动多个进程,并且还能绑定不同的端口
- ❓问题
- 每个子进程都会从父进程中复制一份IOLoop的实例,如果在创建子进程前修改了IOLoop,会影响所有的子进程
- 所有的进程都是由一个命令启动的,无法做到在不停止服务的情况下修改代码
- 所有进程共享一个端口,想要分别监控很困难
options
-
tornado为我们提供了一个
tornado.options
模块 -
作用:全局参数的定义、存储、转换
-
基础方法与属性
-
tornado.options.define()
-
原型
1
2
3
4
5
6
7
8
9
10tornado.options.define(
name: str,
default: Any = None,
type: type = None,
help: str = None,
metavar: str = None,
multiple: bool = False,
group: str = None,
callback: Callable[[Any], None] = None,
) -
功能:用来定义options选项变量的方法
-
参数
name
:选项变量名,必须保证其唯一性,否则会报options xxx already define in ...
default
:设置选项变量的默认值,默认为Nonetype
- 设置选项变量的类型,从命令行或配置文件导入参数时tornado会根据类型转换输入的值,转换不成会报错,可以是str、float、int、datetime、timedelta
- 如果没有设置type,会根据default的值进行转换
- 如果default没有设置,那么不进行转换
multiple
:设置选项变量是否可以为多个值,默认为falsehelp
:选项变量的帮助提示信息
-
示例
1
2tornado.options.define('port', default=8000, type=int)
tornado.options.define('list', default=[], type=str)
-
-
tornado.options.options
- 全局的options对象,所有定义的选项都会作为该对象的属性
-
-
获取参数的方法(3种)
-
tornado.options.parse_command_line()
-
作用:转换命令行参数,
-
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
# 定义两个参数
tornado.options.define('port', default=8000, type=int)
tornado.options.define('list', default=[], type=str, multiple=True)
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.write('sunck is a good man!')
if __name__ == '__main__':
# 转换命令行参数,并保存到tornado.options.options
tornado.options.parse_command_line()
print('list =', tornado.options.options.list)
app = tornado.web.Application([
(r'/', IndexHandler)
])
httpServer = tornado.httpserver.HTTPServer(app)
# 使用变量的值
httpServer.bind(tornado.options.options.port)
httpServer.start(1)
tornado.ioloop.IOLoop.current().start() -
启动
1
python server04.py --port=8848 --list=good,nice,handsome,cool
-
-
tornado.options.parse_config_file(path)
-
作用:从配置文件导入参数
-
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
# 定义两个参数
tornado.options.define('port', default=8000, type=int)
tornado.options.define('list', default=[], type=str, multiple=True)
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.write('sunck is a good man!')
if __name__ == '__main__':
# 转换命令行参数,并保存到tornado.options.options
tornado.options.parse_config_file('config')
print('list =', tornado.options.options.list)
app = tornado.web.Application([
(r'/', IndexHandler)
])
httpServer = tornado.httpserver.HTTPServer(app)
# 使用变量的值
httpServer.bind(tornado.options.options.port)
httpServer.start(1)
tornado.ioloop.IOLoop.current().start() -
需要创建一个名为config的普通文件
1
2port = 7000
list = ['good', 'nice', 'handsome']说明:
- 书写格式仍需要按照python的语法要求
- 不支持字典类型
-
启动
1
python server05.py
-
-
最终版本
-
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import tornado.web
import tornado.ioloop
import tornado.httpserver
import config
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.write('sunck is a good man!')
if __name__ == '__main__':
print('list =', config.options['list'])
app = tornado.web.Application([
(r'/', IndexHandler)
])
httpServer = tornado.httpserver.HTTPServer(app)
# 使用变量的值
httpServer.bind(config.options['port'])
httpServer.start(1)
tornado.ioloop.IOLoop.current().start() -
需要创建一个名为
config.py
的普通文件1
2
3
4
5# 参数
options = {
'port': 8080,
'list': ['good', 'nice', 'handsome']
}
-
-
-
日志
-
当我们在大马中使用
parse_command_line()
或者parse_config_file(path)
方法时,tornado会默认开启logging模块功能,向屏幕终端输出一些打印信息 -
关闭日志(2种方法)
-
在第一行加入
tornado.options.options.logging = None
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29import tornado.web
import tornado.ioloop
import tornado.httpserver
import tornado.options
# 定义两个参数
tornado.options.define('port', default=8000, type=int)
tornado.options.define('list', default=[], type=str, multiple=True)
class IndexHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.write('sunck is a good man!')
if __name__ == '__main__':
# 转换命令行参数,并保存到tornado.options.options
tornado.options.options.logging = None
tornado.options.parse_config_file('config')
print('list =', tornado.options.options.list)
app = tornado.web.Application([
(r'/', IndexHandler)
])
httpServer = tornado.httpserver.HTTPServer(app)
# 使用变量的值
httpServer.bind(tornado.options.options.port)
httpServer.start(1)
tornado.ioloop.IOLoop.current().start() -
黑屏终端
1
python server04.py --port=8848 --list=good,nice,handsome,cool --logging=none
-
-