Tornado——入门篇

第一天(hello tornado)

环境

习惯用python2,所以安装6.0以下版本的tornado(6.0以上最低3.5)
pip install tornado==5.1.1

demo跑起来

在这里插入图片描述
执行python ./test1.py

测试一下

curl是基于URL语法在命令行方式下工作的文件传输工具,它支持FTP,FTPS,HTTP,HTTPS,GOPHER,TELNET,DICT,FILE及LDAP等协议。curl支持HTTPS认证,并且支持HTTP的POST,PUT等方法,FTP上传,kerberos认证,HTTP上传,代理服务器,cookies,用户名/密码认证,通过http代理服务器上传文件到FTP服务器等等,功能十分强大。

  • -A/–user-agent 设置用户代理发送给服务器,即告诉服务器浏览器为什么
  • -basic 使用HTTP基本验证
  • –tcp-nodelay 使用TCP_NODELAY选项
  • -e/–referer 来源网址,跳转过来的网址
  • –cacert 指定CA证书 (SSL)
  • –compressed 要求返回是压缩的形势,如果文件本身为一个压缩文件,则可以下载至本地
  • -H/–header 自定义头信息传递给服务器
  • -I/–head 只显示响应报文首部信息
  • –limit-rate 设置传输速度
  • -u/–user <user[:password]>设置服务器的用户和密码
  • -0/–http1.0 使用HTTP 1.0

curl -X PUT www.baidu.com
curl -X DELETE www.baidu.com
curl -X POST www.baidu.com -d “key=value&key1=value1”
curl -X GET www.baidu.com
-X 指定请求方式 -d 添加参数

curl localhost:8000/
Hello, Welcome to the world of tornado!
curl -X POST localhost:8000/
My name is tornado !
关于代码

上述的一个简单web服务主要包含了两个模块

  • tornado.web 这是一个tornado中的web模块

    • RequestHandler
      不同于django,tornado将request与response都封装在了requesthandler中,它封装了对应一个请求的所有信息和方法,write方法是写入响应信息;对于请求方式不同,将不同的逻辑写入到与方法同名的成员方法中,当未重写同名的成员方法时,将会返回 405 方法不被准许错误。
    • Application
      它是tornado web框架的核心应用类,是与服务器对接的接口,保存有路由信息表,其初始化接收的第一个参数就是一个路由信息映射元组的列表;其listen(端口)方法用来创建一个http服务器实例,并绑定到给定端口(注意:此时服务器并未开启监听)。
  • tornado.ioloop tornado的核心io循环模块
    它封装了Linux的epoll和BSD的kqueue,tornado高性能的基石。
    在这里插入图片描述

    • IOLoop.current() 返回当前线程的IOLoop实例。
    • IOLoop.start() 启动IOLoop实例的I/O循环,同时服务器监听被打开。
  • tornado.options 从命令行中读取设置
    它的用法如下:

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)
.
.
.
tornado.options.parse_command_line()
.
http_server.listen(options.port)
.

如果一 个与 define 语句中同名的设置在命令行中被给出,那么它将成为全局 options 的一个 属性。如果用户运行程序时使用了–help 选项,程序将打印出所有你定义的选项以及 你在 define 函数的 help 参数中指定的文本。如果用户没有为这个选项指定值,则使 用default的值进行代替。Tornado使用type参数进行基本的参数类型验证,当不合

适的类型被给出时抛出一个异常。因此,我们允许一个整数的 port 参数作为 options.port 来访问程序。如果用户没有指定值,则默认为 8000。

总结

1 创建请求处理类,继承handler类,重写相应方法
2 创建web应用实例对象,定义路由映射列表
3 绑定端口
4 开启监听服务

Tornado——第二天(关于端口绑定)

回顾

在创建完一个基础的web应用后,我们使用 app.listen() 方法来将 web服务与端口绑定。
这个地方的listen() 方法只是一个封装后的简写形式。
这个绑定过程的原始形式如下:

# app.listen(8000)
http_server = tornado.httpserver.HTTPServer(app) 
http_server.listen(8000)

首先使用了tornado中的http模块为 app 创建了一个 http服务实例。然后将这个服务与8000端口绑定

开启多进程

我们上述都是为tornado开启了一个进程,如果想开启多个线程的话,需要做以下操作:

http_server = tornado.httpserver.HTTPServer(app) 
http_server.bind(8000)
http_server.start(0)

http_server.start(num_processes=1)方法指定开启几个进程,参数num_processes默认值为1,即默认仅开启一个进程;如果num_processes为None或者<=0,则自动根据机器硬件的cpu核芯数创建同等数目的子进程;如果num_processes>0,则创建num_processes个子进程。
而以前使用的简写形式与listen形式则相当于

http_server.bind(8000)
http_server.start(1)

关于多进程形式,因为子进程是从父进程中复制的ioloop实例,所以在创建子进程前如果更改了父进程的ioloop实例,那么每一个子进程都将受影响。

Tornado——第三天(参数传递)

tornado获取参数大致有三种形式:

  • 路由表中正则获取
# _*_ coding:utf-8 _*_

import tornado.web
import tornado.ioloop
import tornado.httpserver
from tornado.options import define,options

# 定义初始端口(默认8000)
define("port", default=8000,help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):
	
	def get(self,params1):
		self.write("i get a params: %s"%params1)


if __name__ == "__main__":
	tornado.options.parse_command_line()
	app = tornado.web.Application(
                handlers=[
                    (r"/(\w*)", IndexHandler),
                    ]
                )
        http_server = tornado.httpserver.HTTPServer(app)
        http_server.listen(options.port)
        tornado.ioloop.IOLoop.current().start()

在这里插入图片描述

  • 通过内置方法获取
# _*_ coding:utf-8 _*_
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

class IndexHandler(tornado.web.RequestHandler):
   def get(self,params1):
   		self.write("i get a params: %s"%params1)
        params2 = self.get_query_argument("params2","None",True)
        self.write("get_query_argument获取参数是:%s"%params2)
        params3 = self.get_query_arguments("params2",True)
        self.write("get_query_arguments获取参数是:%s"%params3)
               
# 获取get参数
# get_query_argument(name, default=_ARG_DEFAULT, strip=True)
# get_query_arguments(name, strip=True)
# 区别在于如果有多个同名参数,第一个方法返回最后一个值,未传参数的情况且未设置默认值将会抛出异常。第二个方法返回一个列表,未传参数将返回一个空列表。

在这里插入图片描述
在这里插入图片描述

  • 从request.body中获取
    与获取get参数方式类似,有以下两种方法
get_body_argument(name, default=_ARG_DEFAULT, strip=True)
get_body_arguments(name, strip=True)

** 对于请求体中的数据要求为字符串,且格式为表单编码格式(与url中的请求字符串格式相同),即key1=value1&key2=value2,HTTP报文头Header中的"Content-Type"为application/x-www-form-urlencoded 或 multipart/form-data。对于请求体数据为json或xml的,无法通过这两个方法获取。**

  • 聚合方法
get_argument(name, default=_ARG_DEFAULT, strip=True)
# 相当于 get_query_argument 与 get_body_argument
get_arguments(name, strip=True)
# 相当于 get_query_arguments 与 get_body_arguments

Tornado——第四天(页面模版与静态文件)

#_*_coding: utf-8 _*_
import os
import tornado.web
import tornado.httpserver
import tornado.ioloop
from tornado.options import define,options
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

define("port", default=8000,help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):

  def get(self):
      name = self.get_argument("name","python",True)
      self.render('index1.html',name=name)

if __name__ == "__main__":
  tornado.options.parse_command_line()
  base_dir = os.path.dirname(__file__)
  handlers = [(r"/", IndexHandler),]
  settings = {
          "template_path": os.path.join(base_dir, "templates"),
          "static_path": os.path.join(base_dir, "static"),
          "debug":True
          }
  app = tornado.web.Application(handlers,**settings)
  http_server = tornado.httpserver.HTTPServer(app)
  http_server.listen(options.port)
  tornado.ioloop.IOLoop.current().start()
# index1.html
<!DOCTYPE html>
<html>
<head>
	<title>首页</title>
	<link rel="stylesheet" href="{{static_url("index1.css")}}">
</head>
<body>
	<h1>Hello world !</h1>
	<h3>hello {{name}}</h3>
</body>
</html>
# index1.css
h1 {
	color:red
}

h3 {
	color:green
}

在这里插入图片描述
关于模版方面,tornado使用的是 jinja2 , 使用起来和django是很相似的。

Tornado——第五天(文件上传与下载)

继续编写昨天的代码

class IndexHandler(tornado.web.RequestHandler):

    def initialize(self, upload, download):
        self.upload = upload
        self.download = download

    def get(self):
        name = self.get_argument("name","python",True)
        get_img = self.get_argument("get_img","false",True)
        if get_img != "true":
            self.render('index2.html',name=name)
        else:
        	# set_header方法设置header头
            self.set_header('Content-Type','application/octet-stream')
            self.set_header('Content-Disposition', 'attachment; filename="随机图片.png"')
            img_list = os.listdir(self.download)
            img_path = random.choice(img_list)
            with open(os.path.join(self.download,img_path),'rb') as f:
                while True:
                    data = f.read(4096)
                    if not data:
                        break
                    self.write(data)

    def post(self):
        print "已接收到请求"
        files = self.request.files
        # {"head_img":[{"filename":"..","content_type":"...","body":"..."}]}
        head_img_obj = files.get('head_img')[0]
        if head_img_obj:
            head_img_path = os.path.join(self.upload, head_img_obj['filename'])
            with open(head_img_path,'wb') as f:
                f.write(head_img_obj["body"])
        self.write("success !")

if __name__ == "__main__":
    tornado.options.parse_command_line()
    base_dir = os.path.dirname(__file__)
    settings = {
            "template_path": os.path.join(base_dir, "templates"),
            "static_path": os.path.join(base_dir, "static"),
            "debug":True
            }
    handlers = [
            (r"/", IndexHandler, dict(
                upload=os.path.join(settings["static_path"],"upload"), download=os.path.join(settings["static_path"],"download")
                )),
            ]

Tornado——第六天(同步与异步)

此本分主要实现一个同步与异步的请求功能

# index_asyn.html
<!DOCTYPE html>
<html>
	<head>
		<title>异步测试页面</title>

	</head>
	<body>
		<input id="search" type="text" placeholder="请输入检索关键字" />
		<input  type="button" onclick="request()" value="检索" />
		<div id="result">
			<p>检索结果</p>
			<table id="res_list"></table>
		</div>
		<script type="text/javascript">
			function request(){
				var result = document.getElementById("res_list");
				var search = document.getElementById("search").value;
				var ajax = new XMLHttpRequest();
				ajax.open("post","/", true)
				ajax.setRequestHeader("Content-Type", "application/json");
				ajax.onreadystatechange = function () {
					if(ajax.readyState == 4){
						if(ajax.status == 200){
							res_obj=JSON.parse(ajax.responseText).result;
							result.innerHTML = "";
							for(var i=0; i<res_obj.length;i++){
								html_text = "<tr>"+
									"<td>"+res_obj[i][0]+"<td/>"+
									"<td>"+res_obj[i][1]+"<td/>"+
									"</tr>"
								result.innerHTML = result.innerHTML + html_text							};
						};
					};
				};
				ajax.send(JSON.stringify({"search":search}));
			};
		</script> 
	</body>
</html>  
#_*_coding: utf-8 _*_
import os
import json
import tornado.web
import tornado.httpserver
import tornado.ioloop
from tornado.httpclient import HTTPClient,AsyncHTTPClient
from tornado.options import define,options
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

define("port", default=8000,help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('index_asyn.html')

    # 同步版
   # def post(self):
   #     if self.get_argument("search",False):
   #         search_text = self.get_argument("search")
   #     else:
   #         search_text = json.loads(self.request.body)["search"]
   #     client = HTTPClient()
   #     res = client.fetch("https://suggest.taobao.com/sug?code=utf-8&q=%s"%search_text)
   #     context = res.body.decode("utf8")
   #     self.write(context)

    # 异步版
   # @tornado.web.asynchronous
   # def post(self):
   #     if self.get_argument("search",False):
   #         search_text = self.get_argument("search")
   #     else:
   #         search_text = json.loads(self.request.body)["search"]
   #     client = AsyncHTTPClient()
   #     res = client.fetch("https://suggest.taobao.com/sug?code=utf-8&q=%s"%search_text, callback=self.deal_response)
   #     
   # def deal_response(self,response):
   #     content = response.body.decode("utf8")
   #     self.write(content)
   #     self.finish()
    
    @tornado.web.asynchronous
    @tornado.gen.engine
    def post(self):
        if self.get_argument("search",False):
            search_text = self.get_argument("search")
        else:
            search_text = json.loads(self.request.body)["search"]
        client = AsyncHTTPClient()
        res = yield tornado.gen.Task(client.fetch,"https://suggest.taobao.com/sug?code=utf-8&q=%s"%search_text)
        content = res.body.decode("utf8")
        self.write(content)
        self.finish()

if __name__ == "__main__":
    tornado.options.parse_command_line()
    base_dir = os.path.dirname(__file__)
    handlers = [(r"/", IndexHandler),]
    settings = {
            "template_path": os.path.join(base_dir, "templates"),
            "static_path": os.path.join(base_dir, "static"),
            "debug":True
            }
    app = tornado.web.Application(handlers,**settings)
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.current().start()

在这里插入图片描述
压力测试:
siege 'http://localhost:8000/ POST search=af' -c10 -t10s

  • 同步在这里插入图片描述
  • 异步
    在这里插入图片描述
    结果:
    在10并发的情况下测试10s中
    同步处理了 47 个请求 异步处理了408个请求
    同步平均响应时间为1.74s,异步平均响应时间为0.24s

总结:

  • tornado提供了一个相当成熟的异步web编程解决方案,从耗时的操作中解放出来,转而去处理更多的请求。
  • 关于self.write()
    • 此方法是将数据写入缓冲区中,并不会直接作为相应返回。同步http对应方法会自动调用self.finish()方法。
    • 此方法会自动检测数据类型,如dict将会转换为json返回。并自动设置content-type header头

Tornado——第六天(websocket)

关于轮询 长轮询 长链接
https://www.cnblogs.com/AloneSword/p/3517463.html

轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。
优点:后端程序编写比较容易。
缺点:请求中有大半是无用,浪费带宽和服务器资源。
实例:适于小型应用。

长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。
优点:在无消息的情况下不会频繁的请求,耗费资源小。
缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护。
实例:WebQQ、Hi网页版、Facebook IM。

长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求或是采用xhr请求,服务器端就能源源不断地往客户端输入数据。
优点:消息即时到达,不发无用请求;管理起来也相对方便。
缺点:服务器维护一个长连接会增加开销。
实例:Gmail聊天

Flash Socket:在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信,JavaScript在收到服务器端传送的信息后控制页面的显示。
优点:实现真正的即时通信,而不是伪即时。
缺点:客户端必须安装Flash插件;非HTTP协议,无法自动穿越防火墙。
实例:网络互动游戏。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值