1.1 Tornado是什么?

Tornado是使用Python编写的一个强大的、可扩展的Web服务器。它在处理严峻的网络流量时表现得足够强健,但却在创建和编写时有着足够的轻量级,并能够被用在大量的应用和工具中。

我们现在所知道的Tornado是基于Bret Taylor和其他人员为FriendFeed所开发的网络服务框架,当FriendFeed被Facebook收购后得以开源。不同于那些最多只能达到10,000个并发连接的传统网络服务器,Tornado在设计之初就考虑到了性能因素,旨在解决C10K问题,这样的设计使得其成为一个拥有非常高性能的框架。此外,它还拥有处理安全性、用户验证、社交网络以及与外部服务(如数据库和网站API)进行异步交互的工具。

延伸阅读:C10K问题

基于线程的服务器,如Apache,为了传入的连接,维护了一个操作系统的线程池。Apache会为每个HTTP连接分配线程池中的一个线程,如果所有的线程都处于被占用的状态并且尚有内存可用时,则生成一个新的线程。尽管不同的操作系统会有不同的设置,大多数Linux发布版中都是默认线程堆大小为8MB。Apache的架构在大负载下变得不可预测,为每个打开的连接维护一个大的线程池等待数据极易迅速耗光服务器的内存资源。

大多数社交网络应用都会展示实时更新来提醒新消息、状态变化以及用户通知,这就要求客户端需要保持一个打开的连接来等待服务器端的任何响应。这些长连接或推送请求使得Apache的最大线程池迅速饱和。一旦线程池的资源耗尽,服务器将不能再响应新的请求。

异步服务器在这一场景中的应用相对较新,但他们正是被设计用来减轻基于线程的服务器的限制的。当负载增加时,诸如Node.js,lighttpd和Tornodo这样的服务器使用协作的多任务的方式进行优雅的扩展。也就是说,如果当前请求正在等待来自其他资源的数据(比如数据库查询或HTTP请求)时,一个异步服务器可以明确地控制以挂起请求。异步服务器用来恢复暂停的操作的一个常见模式是当合适的数据准备好时调用回调函数。我们将会在第五章讲解回调函数模式以及一系列Tornado异步功能的应用。

自从2009年9月10日发布以来,TornadoTornado已经获得了很多社区的支持,并且在一系列不同的场合得到应用。除FriendFeed和Facebook外,还有很多公司在生产上转向Tornado,包括Quora、Turntable.fm、Bit.ly、Hipmunk以及MyYearbook等。

总之,如果你在寻找你那庞大的CMS或一体化开发框架的替代品,Tornado可能并不是一个好的选择。Tornado并不需要你拥有庞大的模型建立特殊的方式,或以某种确定的形式处理表单,或其他类似的事情。它所做的是让你能够快速简单地编写高速的Web应用。如果你想编写一个可扩展的社交应用、实时分析引擎,或RESTful API,那么简单而强大的Python,以及Tornado正是为你准备的!

tornado和其他的框架如django,flask相比优缺点

Django

优点:

  • 大和全(重量级框架)
  • 自带orm,template,view
  • 需要的功能也可以去找第三方的app
  • 注重高效开发
  • 全自动化的管理后台(只需要使用起ORM,做简单的定义,就能自动生成数据库结构,全功能的管理后台)
  • session功能

缺点:

  • template不怎么好用(来自自身的缺点)
  • 数据库用nosql不方便(来自自身的缺点)
  • 如果功能不多,容易臃肿

Tornado

优点:

  • 少而精(轻量级框架)
  • 注重性能优越,速度快
  • 解决高并发(请求处理是基于回调的非阻塞调用)
  • 异步非阻塞
  • websockets 长连接
  • 内嵌了HTTP服务器
  • 单线程的异步网络程序,默认启动时根据CPU数量运行多个实例;利用CPU多核的优势
  • 自定义模块

缺点:

  • 模板和数据库部分有很多第三方的模块可供选择,这样不利于封装为一个功能模块

总结:

要性能, Tornado 首选;要开发速度,Django 和 Flask 都行,区别是 Flask 把许多功能交给第三方库去完成了,因此 Flask 更为灵活。

综上所述:

Django适合初学者或者小团队的快速开发,适合做管理类、博客类网站、或者功能十分复杂需求十分多的网站

Tornado适合高度定制,适合访问量大,异步情况多的网站

选择适合的才是最好的

1.2快速入门

这里我就不写安装步骤了,pip3 install tornado

hello,Tornado     从get请求例子开始

import tornado
from tornado import ioloop,httpserver
from tornado.web import RequestHandler,url
from tornado.options import define,options tornado.options.define("port",type=int,default=8002,help="服务器客户端") #默认配置
class IndexHandler(RequestHandler):
def get(self):#get请求
self.write("hello Tornado")  #发送hello Tornado到缓冲区,可以发送多个,这里到最后会默认执行self.finish(),将所有缓冲区信息发送给报文
#入口文件
if __name__ == '__main__':
tornado.options.parse_command_line()
app = tornado.web.Application([(r"/",IndexHandler),],
debug=True) #路由映射
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)      #监听8002端口
tornado.ioloop.IOLoop.current().start() #开始循环监听

通过上面代码我们了解了tornado运行过程

tornado的基础web框架模块

RequestHandler
封装了对应一个请求的所有信息和方法,write(响应信息)就是写响应信息的一个方法;对应每一种http请求方式(get、post等),把对应的处理逻辑写进同名的成员方法中(如对应get请求方式,就将对应的处理逻辑写在get()方法中),当没有对应请求方式的成员方法时,会返回“405: Method Not Allowed”错误。

Application
Tornado Web框架的核心应用类,是与服务器对接的接口,里面保存了路由信息表,其初始化接收的第一个参数就是一个路由信息映射元组的列表;其listen(端口)方法用来创建一个http服务器实例,并绑定到给定端口(注意:此时服务器并未开启监听)

Tornado核心IOLoop循环模块

tornado的核心io循环模块,封装了Linux的epoll和BSD的kqueue,tornado高性能的基石。 以Linux的epoll为例,其原理如下图:

IOLoop.current() 返回当前线程的IOLoop实例。

IOLoop.start() 启动IOLoop实例的I/O循环,同时服务器监听被打开。

小结:

  1. tornado高性能Web原理是利用Linux epoll IO多路模型和协程异步编程

  2. tornado Web框架核心模块是 web 和 核心事件循环模块是 IOLoop

1.3基础应用

上面的例子是get请求,下面我们再来看一下post请求如何做

Post请求

import tornado
from tornado import ioloop,httpserver
from tornado.web import RequestHandler,url
from tornado.options import define,options tornado.options.define("port",type=int,default=8002,help="服务器客户端")
class IndexHandler(RequestHandler):
def post(self):
# a = self.get_body_arguments("a")
a = self.get_body_argument("a")
print(a)
self.write("a的值为%s"%a)
#入口文件
if __name__ == '__main__':
tornado.options.parse_command_line()
app = tornado.web.Application([(r"/",IndexHandler),],
debug=True)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()

使用RequestHandler对象的get_argument方法来捕获请求查询字符串的的参数。

get_argument可以获取post请求和get请求的全部参数。

get_query_argument获取get请求的所有参数。

get_body_argument获取get请求的所有参数。

这里需要注意的是上面的三个方法如果遇到相同的查询字符串,会以最后查询到的字符串值为结果。

这里返回的是最后一个a的值,大家可能会有一问,如果我想获取所有a的值,改怎么做呢。

很简单,上面代码中有个方法我给注释了,没错,就是get_body_arguments,方法会返回所有的a的结果数组。

Post发送json请求

在tornado中post请求接收的参数只允许使用两种格式

但是如果想发送json请求,我们就需要自定义解析json文件的过程

# coding:utf-8
import tornado
from tornado import ioloop,httpserver
from tornado.web import RequestHandler,url
from tornado.options import define,options
import json,time
tornado.options.define("port",type=int,default=8002,help="服务器客户端")
class IndexHandler(RequestHandler): def post(self):
#json请求 解析
if self.request.headers.get("Content-Type").startswith("application/json"): #判断请求格式
json_data = self.request.body #获取请求json
json_data = str(json_data,'utf-8') #转换字符串
json_args = json.loads(json_data) #转换字典
self.write(json_args) #入口文件
if __name__ == '__main__':
tornado.options.parse_command_line()
app = tornado.web.Application([(r"/",IndexHandler),
],
debug=True)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()

request.headers.get方法用来获取报头信息,request.body获取body。

结果:

Post请求上传文件

import tornado
from tornado import ioloop, httpserver
from tornado.web import RequestHandler, url
from tornado.options import define, options
import json, time tornado.options.define("port", type=int, default=8002, help="服务器客户端") class IndexHandler(RequestHandler): def post(self):
# 上传文件
image1 = self.request.files["image1"][0]["body"]
with open("image1.jpg",'wb')as f: #文件保存
f.write(image1)
f.close()
self.write("上传成功") # 入口文件
if __name__ == '__main__':
tornado.options.parse_command_line()
app = tornado.web.Application([(r"/", IndexHandler) ],
debug=True)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()

request.files方法可以接受上传文件,我们需要做的就是保存文件操作。

使用表单格式可以上传文件如图:

set_header方法自定义请求头

import tornado
from tornado import ioloop, httpserver
from tornado.web import RequestHandler, url
from tornado.options import define, options
import json, time tornado.options.define("port", type=int, default=8002, help="服务器客户端") class IndexHandler(RequestHandler):
# 自定义header
def set_default_headers(self):
self.set_header("Content", "application/json; charset=UTF-8")
self.set_header("itcast", "python") def get(self): # 返回json数据
stu = {
"name": 'zhaozhi',
"age": 24,
'sex': "男"
}
self.write(stu) # 入口文件
if __name__ == '__main__':
tornado.options.parse_command_line()
app = tornado.web.Application([(r"/", IndexHandler), ],
debug=True)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()

函数set_default_headers函数是RequestHanlder的方法,用于设置全局默认header。

如果在get请求中出现set_header方法并且定义header属性和全局设置有相同,以get请求局部设置的header属性值为优先。

Http状态码

可以使用RequestHandler类的set_status()方法自定义设置HTTP状态码。有很多定义好的状态码,例如:

404 Not Found

Tornado会在HTTP请求的路径无法匹配任何RequestHandler类相对应的模式时返回404(Not Found)响应码。

400 Bad Request

如果你调用了一个没有默认值的get_argument函数,并且没有发现给定名称的参数,Tornado将自动返回一个400(Bad Request)响应码。

405 Method Not Allowed

如果传入的请求使用了RequestHandler中没有定义的HTTP方法(比如,一个POST请求,但是处理函数中只有定义了get方法),Tornado将返回一个405(Methos Not Allowed)响应码。

500 Internal Server Error

当程序遇到任何不能让其退出的错误时,Tornado将返回500(Internal Server Error)响应码。你代码中任何没有捕获的异常也会导致500响应码。

200 OK

如果响应成功,并且没有其他返回码被设置,Tornado将默认返回一个200(OK)响应码。

set_status()方法有两个参数,第一个为状态码,第二个为响应结果(已有状态码可以不设置此参数,新的状态码必须设置)。这里就不给示例了,可以自己尝试写一下

你想使用自己的方法代替默认的错误响应,你可以重写write_error方法在你的RequestHandler类中。(检测到异常会抛出)

我们也可以在get方法中使用send_error方法来直接抛出异常。这两种方法和set_status方法参数一致。

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web from tornado.options import define, options
define("port", default=8002, help="run on the given port", type=int) class IndexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument('greeting', 'Hello')
self.write(greeting + ', friendly user!')
def write_error(self, status_code, **kwargs):
self.write("Gosh darnit, user! You caused a %d error." % status_code) if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()

redirect方法实现登录跳转

import tornado
from tornado import ioloop, httpserver
from tornado.web import RequestHandler, url
from tornado.options import define, options tornado.options.define("port", type=int, default=8002, help="服务器客户端") class IndexHandler(RequestHandler): def get(self):
self.write("登录成功") #登录模块
class LoginHandler(RequestHandler):
def get(self):
self.write("<form method='post'><input type='submit' value='登录'></form> ") def post(self):
self.redirect('/') # 入口文件
if __name__ == '__main__':
tornado.options.parse_command_line()
app = tornado.web.Application([(r"/", IndexHandler),
(r"/login", LoginHandler),
],
debug=True)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()

不知道有小伙伴注意到了没,关于debug=True,调试模式,为什么修改代码后不会自动重启。

没找到原因(知道原因的小伙伴评论区解释一下),不过找到了另一个方法,import tornado.autoreload,导入模块就可以自动重启了,有兴趣的小伙伴可以取看看源码。这里就不解释了。

本文部分参考tornado中文文档 http://demo.pythoner.com/itt2zh/ch1.html

本章节到这里就结束了,谢谢大家阅读,感兴趣的话加个关注,后续还会继续更新。

Tornado基础学习篇的更多相关文章

  1. 鸟哥Linux私房菜基础学习篇学习笔记3

    鸟哥Linux私房菜基础学习篇学习笔记3 第十二章 正则表达式与文件格式化处理: 正则表达式(Regular Expression) 是通过一些特殊字符的排列,用以查找.删除.替换一行或多行文字字符: ...

  2. 鸟哥Linux私房菜基础学习篇学习笔记2

    鸟哥Linux私房菜基础学习篇学习笔记2 第九章 文件与文件系统的压缩打包: Linux下的扩展名没有什么特殊的意义,仅为了方便记忆. 压缩文件的扩展名一般为: *.tar, *.tar.gz, *. ...

  3. 鸟哥Linux私房菜基础学习篇学习笔记1

    鸟哥Linux私房菜基础学习篇学习笔记1 第三章 主导分区(MBR),当系统在开机的时候会主动去读取这个区块的内容,必须对硬盘进行分区,这样硬盘才能被有效地使用. 所谓的分区只是针对64Bytes的分 ...

  4. 拒绝从入门到放弃_《鸟哥的 Linux 私房菜 — 基础学习篇(第三版)》必读目录

    目录 目录 前言 关于这本书 必看知识点 最后 前言 相信部分刚进入这个行业的新同学会对一个问题感到疑惑,为什么从培训学校出来的学员不被欢迎? 这里记录下一些我个人的看法(博主也曾有面试新员工的经历) ...

  5. 【音视频连载-001】基础学习篇- SDL 介绍以及工程配置

    技术开发故事会连载 这是音视频基础学习系列的第一篇文章,主要讲解 SDL 是什么以及为什么要用到它,看似和音视频没啥卵关系,其实必不可少. SDL 简介 SDL 是 "Simple Dire ...

  6. Git 基础学习篇(应用-windows篇)

    此篇教程主要是讲应用,因为理论,,,额,我也说不出来.大家要深入学习还是看廖老师的教程吧. 可以把这篇当作一个简单应用的参考,因为当初看廖老师的也难看啊!!! 以下是资料: 廖雪峰-Git教程 [Gi ...

  7. Linux 基础学习篇 序篇

    读序篇可以知道的: 1.有些指令知道前和知道后,自己的操作是完全不同的,可能知道前,会用reset把系统重新启动一遍,而知道后会使用ps和kill来关闭进程. 2.如果对Linus的学习知识" ...

  8. JS基础学习篇(一)

    近来一直在学习js和jquery.刚刚进入前端工作还没有多久,虽然大学里学习的是编程自认为也学的还可以,但前端接触的不多,一直认为前端十分简单.其实不然,特别是工作的时候要自己设计一个完整的项目前端, ...

  9. Python 基础学习篇

    注:技术尚浅,时间匆忙,如有错误或者不当之处值得商榷的,请留言,吾必思而改之. 第一篇 :Python基础- 安装/变量/输入/及循环语句使用 第二篇:  Python基础- 常用数据类型 第三篇: ...

随机推荐

  1. 使用Python爬取淘宝两千款套套

    各位同学们,好久没写原创技术文章了,最近有些忙,所以进度很慢,给大家道个歉. 警告:本教程仅用作学习交流,请勿用作商业盈利,违者后果自负!如本文有侵犯任何组织集团公司的隐私或利益,请告知联系猪哥删除! ...

  2. Codeforces Round #503 (by SIS, Div. 2) D. The hat -交互题,二分

    cf1020D 题意: 交互题目,在有限的询问中找到一个x,使得数列中的第x位和第(x+n/2)位的值大小相同.数列保证相邻的两个差值为1或-1: 思路: 构造函数f(x) = a[x] - a[x ...

  3. hdu 3974 Assign the task(线段树)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=3974 题意:给定一棵树,50000个节点,50000个操作,C x表示查询x节点的值,T x y表示更 ...

  4. java 高薪计划

    一.基础 集合类,并发包,IO/NIO,JVM,内存模型,泛型,异常,反射,等有深入了解,最好是看过源码了解底层的设计. 二.需要全面的互联网主流技术相关知识 深入了解mysql,redis,mong ...

  5. Django学习笔记(一):第一个django程序

    1.创建和运行 django-admin startproject xxx python manage.py runserver 2.第一个项目程序 wsgi.py中文名:python服务器网关接口. ...

  6. Mac环境IntelliJ IDEA配置JDK提示The selected directory is not a valid home for JDK

    笔者使用mac配置如下: 硬件环境:MacBook Pro 操作系统:MacOS Sierra 10.13.6 开发工具:IntelliJ IDEA 2016.x JDK版本:已有Open JDK 8 ...

  7. I don't Blame You that You don't Understand Me

    I don't Blame You that You don't Understand Me Every one has a dead corner in himself, with no entry ...

  8. Salesforce LWC学习(七) Navigation & Toast

    上一篇我们介绍了针对LWC中常用的LDS的适配的wire service以及@salesforce模块提供的相关的service,其实LWC中还提供其他的好用的service,比如针对导航相关的lig ...

  9. (2)RapidJson的详解及使用

        本节主要介绍RapidJson是如何使用的.   (1)RapidJson是什么 RapidJson是一个跨平台的c++的json的解析器和生成器: 相比较jsoncpp库,RapidJson ...

  10. Winform将FastReport的report与PreviewControl建立绑定关系

    场景 FastReport安装包下载.安装.去除使用限制以及工具箱中添加控件: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/10 ...