再次一头扎进Tornado的风暴眼中のイチ

Tornado


目录

认识Tornado

其实没有啥可说的,和Django一样是一个python web框架,但是轻量化、异步。后来由Facebook收购然后开源出去。
啥?Django是啥?和Tornado一样是一个python web框架,大而全。
Tornado是啥?。。。。。。。
。。。。。。
。。。。。。
。。。。。。
Django大而全,开发敏捷。
Tornado小而精,性能优越。

性能优越的原理

这个epoll啊,然后linux注册,有空闲时间,使用yeild,epoll通知,处理等等等等。。。。
原理复杂的我不想讲(其实还是讲不清楚)。。。。

安装

pip,pip,pip啊少年,我还能说啥

  1. pip install tornado

    第一次代码

    说啥都没用,先show 一段code 亮个相。
  • server.py
  1. # tornado的基础web框架模块
  2. import tornado.web
  3. import tornado.ioloop
  4. ``
  5. ``
  6. # 首页处理类定义
  7. # RequestHandler请求处理基础类,处理所有请求的信息和方法
  8. class IndexHandler(tornado.web.RequestHandler):
  9. # get方式请求
  10. # 如果没有定义的请求方法,比如此时是POST方式访问,则或返回405: Method Not Allowed
  11. def get(self):
  12. # 返回响应信息的一个方法,Tornado会返回所规定的信息
  13. self.write("Hello RedQueen!")
  14. ``
  15. ``
  16. if __name__ == "__main__":
  17. # Application是Tornado Web框架的核心应用类,与服务器对接的接口,
  18. # 其中保存路由表,初始化的第一个参数就是路由信息元组/列表
  19. app = tornado.web.Application([
  20. (r"/", IndexHandler),
  21. ])
  22. # listen方法绑定8000端口创建一个http服务器实例
  23. # 此时服务器还未开始监听端口
  24. app.listen(8000)
  25. # 提示信息
  26. print("请访问首页网址http://127.0.0.1:8000")
  27. # ioloop是tornado的核心io循环模块,封装了Linux的epoll和BSD的kqueue,tornado高性能的基石。
  28. # current返回当前线程的IOLoop实例
  29. # start启动IOLoop实例的io循环,此时服务器开始监听端口
  30. tornado.ioloop.IOLoop.current().start()

使用python执行文件就可以了

  1. python server.py

之后你会发现程序在打印完提示信息后就停住了,这是你打开浏览器访问提示的网址就会发现神奇的一幕(其实也没啥神奇的,只是说明Tornado开始工作了。)

HTTP服务器

tornado有自己的HTTP服务器实现方法的,这个实现方法不仅能帮助我们理解tornado的启动方式而且有更加丰富的启动选项。

  1. import tornado.web
  2. import tornado.ioloop
  3. # tornado 的 http模块
  4. import tornado.httpserver
  5. ``
  6. ``
  7. class IndexHandler(tornado.web.RequestHandler):
  8. def get(self):
  9. self.write("Hello RedQueen!")
  10. ``
  11. ``
  12. if __name__ == "__main__":
  13. app = tornado.web.Application([
  14. (r"/", IndexHandler),
  15. ])
  16. # 创建HTTP服务器实例
  17. # 接受应用对象app注册路由映射表
  18. http_server = tornado.httpserver.HTTPServer(app)
  19. # 将端口监听语句从app.listen(8000)修改为以下语句
  20. # 同样的,此时有端口绑定,当时并没有监听
  21. http_server.listen(8000)
  22. # 提示信息
  23. print("请访问首页网址http://127.0.0.1:8000")
  24. tornado.ioloop.IOLoop.current().start()

再次访问网址会发现和之前的结果是相同的。

多进程

注意,这里说的是多进程的实现,并不是多线程。
之前的程序实现的都是单进程的实例,如果我们需要用到多进程的实例怎么办呢?

  1. import tornado.web
  2. import tornado.ioloop
  3. # tornado 的 http模块
  4. import tornado.httpserver
  5. ``
  6. ``
  7. class IndexHandler(tornado.web.RequestHandler):
  8. def get(self):
  9. self.write("Hello RedQueen!")
  10. ``
  11. ``
  12. if __name__ == "__main__":
  13. app = tornado.web.Application([
  14. (r"/", IndexHandler),
  15. ])
  16. http_server = tornado.httpserver.HTTPServer(app)
  17. # 多进程,使用bind进行端口和ip的绑定
  18. # app.listen()只能使用在单进程的实例中
  19. http_server.bind(8000)
  20. # start方法指定开启几个进程,输入参数默认值为1,仅开启一个进程
  21. # 如果为None或者小于等于0,则自动根据机器硬件的cpu核芯数创建同等数目的子进程
  22. # 如果大于0,则创建与输入参数相同个数的子进程。
  23. http_server.start(0)
  24. # 提示信息
  25. print("请访问首页网址http://127.0.0.1:8000")
  26. tornado.ioloop.IOLoop.current().start()

另外:虽然tornado提供了这种开启多进程的方式但是因为:

  1. 每个子进程会复制一份IOLoop实例, 如果在创建子进程前IOLoop实例被改变,会影响到每一个子进程。
  2. 所有进程一次性开启,没有办法不停实现不停服务更新
  3. 所有进程共享一个端口,单独监控每一个进程变得困难

以上原因,所以建议手动开启多个进程,绑定不同的端口。
(什么?你问怎么手动开启?传入不同的端口号,将脚本执行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
修改程序:

  1. import tornado.web
  2. import tornado.ioloop
  3. import tornado.httpserver
  4. # options模块
  5. import tornado.options
  6. ``
  7. # 定义服务器监听端口
  8. tornado.options.define("port", default=8000, type=int, help="run server on the given port")
  9. # 多值情况演示
  10. tornado.options.define("values", default=[], type=str, multiple=True, help="test values")
  11. ``
  12. ``
  13. class IndexHandler(tornado.web.RequestHandler):
  14. def get(self):
  15. self.write("Hello RedQueen!")
  16. ``
  17. ``
  18. if __name__ == "__main__":
  19. app = tornado.web.Application([
  20. (r"/", IndexHandler),
  21. ])
  22. # 解析命令行参数
  23. tornado.options.parse_command_line()
  24. # 输出演示的多值情况
  25. print(tornado.options.options.values)
  26. http_server = tornado.httpserver.HTTPServer(app)
  27. # 监听定义的端口
  28. http_server.listen(tornado.options.options.port)
  29. # 提示信息
  30. print("请访问首页网址http://127.0.0.1:%s" % tornado.options.options.port)
  31. tornado.ioloop.IOLoop.current().start()

执行以下命令就能看到现象了:

  1. python server.py --port=9000 --values=python,c++,java,php,ios

可以看到端口开在了9000端口上,而且console界面也输出了values的值

配置文件参数设置

命令行有的时候也不方便,那可以定义配置文吗?当然也是可以的。
我们在程序的同级目录下新建文件config

  1. port = 8000
  2. values = ["python", "c++", "java", "php", "ios"]

你可能也发现了,配置文件的语法格式和Python的语法格式相同,事实上就是如此。
修改启动程序:

  1. import tornado.web
  2. import tornado.ioloop
  3. import tornado.httpserver
  4. # options模块
  5. import tornado.options
  6. ``
  7. # 定义服务器监听端口
  8. tornado.options.define("port", default=8000, type=int, help="run server on the given port")
  9. # 多值情况演示
  10. tornado.options.define("values", default=[], type=str, multiple=True, help="test values")
  11. ``
  12. ``
  13. class IndexHandler(tornado.web.RequestHandler):
  14. def get(self):
  15. self.write("Hello RedQueen!")
  16. ``
  17. ``
  18. if __name__ == "__main__":
  19. app = tornado.web.Application([
  20. (r"/", IndexHandler),
  21. ])
  22. # 从配置文解析参数
  23. tornado.options.parse_config_file("./config")
  24. # 输出演示的多值情况
  25. print(tornado.options.options.values)
  26. http_server = tornado.httpserver.HTTPServer(app)
  27. # 监听定义的端口
  28. http_server.listen(tornado.options.options.port)
  29. # 提示信息
  30. print("请访问首页网址http://127.0.0.1:%s" % tornado.options.options.port)
  31. tornado.ioloop.IOLoop.current().start()

然后直接执行server.py文件就可以了,可以看到执行的程序参数就是定义的配置文参数。

日志说明

当我们在代码中调用parse_command_line()或者parse_config_file()的方法时,tornado会默认配置标准logging模块,默认开启日志功能,并向标准输出(屏幕)打印日志信息。
如果想关闭tornado默认的日志功能,可以在命令行中添加—logging=none 或者在代码中执行如下操作:

  1. from tornado.options import options, parse_command_line
  2. options.logging = None
  3. parse_command_line()

    配置文件导入配置

    由于使用prase_config_file()的时候,还需要使用tornado.options.define()来进行定义,而且不能使用字典格式。所以使用配置文件的时候,建议新建一个python文件(如config.py),然后在里面直接定义python类型的变量(可以是任何的python类型);在需要配置文件参数的地方,将config.py作为模块导入,并使用其中的变量参数。
    比如我们新建配置文件config.py

  4. #!/usr/bin/env python

  5. import os
  6. ``
  7. # mariadb配置
  8. database = {
  9. 'host': '127.0.0.1',
  10. 'port': 3306,
  11. 'name': 'redqueen',
  12. 'user': 'redqueen',
  13. 'password': 'password',
  14. }
  15. ``
  16. # Tornado app配置
  17. settings = {
  18. 'template_path': os.path.join(os.path.dirname(__file__), 'templates'),
  19. 'static_path': os.path.join(os.path.dirname(__file__), 'statics'),
  20. 'cookie_secret': '8z+&w@86wfck8g0p%@nj8ixr731n!n$mkter4sm3sja6n6bc&1',
  21. 'xsrf_cookies': False,
  22. 'login_url': '/login',
  23. 'debug': True,
  24. }
  25. ``
  26. # 日志路径
  27. log_path = os.path.join(os.path.dirname(__file__), 'logs/all.log')

修改启动程序如下:

  1. import tornado.web
  2. import tornado.ioloop
  3. import tornado.httpserver
  4. import tornado.options
  5. ``
  6. # 导入配置文件
  7. import config
  8. ``
  9. # 定义服务器监听端口
  10. tornado.options.define("port", default=8000, type=int, help="run server on the given port")
  11. ``
  12. ``
  13. class IndexHandler(tornado.web.RequestHandler):
  14. def get(self):
  15. self.write("Hello RedQueen!")
  16. ``
  17. ``
  18. if __name__ == "__main__":
  19. # 将配置文件选项传入应用实例
  20. app = tornado.web.Application([
  21. (r"/", IndexHandler),
  22. ], **config.settings)
  23. tornado.options.parse_command_line()
  24. http_server = tornado.httpserver.HTTPServer(app)
  25. http_server.listen(tornado.options.options.port)
  26. # 提示信息
  27. # python3.6 语法,注意版本,如果无法运行,更新方法就可以了
  28. print(f"请访问首页网址http://127.0.0.1:{tornado.options.options.port}")
  29. tornado.ioloop.IOLoop.current().start()