习惯用python2,所以安装6.0以下版本的tornado(6.0以上最低3.5)
pip install tornado==5.1.1
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
设置服务器的用户和密码 - -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模块
tornado.ioloop tornado的核心io循环模块
它封装了Linux的epoll和BSD的kqueue,tornado高性能的基石。
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 开启监听服务
在创建完一个基础的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获取参数大致有三种形式:
# _*_ 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)
# 区别在于如果有多个同名参数,第一个方法返回最后一个值,未传参数的情况且未设置默认值将会抛出异常。第二个方法返回一个列表,未传参数将返回一个空列表。
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
#_*_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
<html>
<head>
<title>首页title>
head>
<body>
<h1>Hello world !h1>
<h3>hello {{name}}h3>
body>
html>
# index1.css
h1 {
color:red
}
h3 {
color:green
}
关于模版方面,tornado使用的是 jinja2 , 使用起来和django是很相似的。
继续编写昨天的代码
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")
)),
]
此本分主要实现一个同步与异步的请求功能
# index_asyn.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 = ""+
""+res_obj[i][0]+" "+
""+res_obj[i][1]+" "+
" "
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
总结:
关于轮询 长轮询 长链接
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协议,无法自动穿越防火墙。
实例:网络互动游戏。