Web服务器系列相关文章编写如下:
Tornado龙卷风是一个开源的网络服务器框架(一个python web框架和异步网络库),它是基于社交聚合网站FriendFeed的实时信息服务开发而来的。2007年由4名Google前软件工程师一起创办了FriendFeed,旨在使用户能够方便地跟踪好友在Facebook和Twitter等多个社交网站上的活动。
Tornado的主要功能模块:
其中Tornado的服务器三个底层核心模块:
https://github.com/tornadoweb/tornado
https://www.tornadoweb.org/en/stable/
Tornado与标准库集成 asyncio 模块和共享相同的事件循环(默认情况下,从Tornado 5.0开始)。通常,设计用于 asyncio 可以与 Tornado 自由混合。
pip install tornado
pip install tornado==5.1.1
tornado.web提供了一个具有异步功能的简单 Web 框架,允许它扩展到大量打开的连接,使其成为长轮询的理想选择。
import asyncio
import tornado.web
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world, 爱看书的小沐, 2022!")
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
])
async def main():
app = make_app()
app.listen(8888)
await asyncio.Event().wait()
if __name__ == "__main__":
asyncio.run(main())
或者
import tornado.ioloop
import tornado.web
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world, 爱看书的小沐, 2022!")
if __name__ == "__main__":
application = tornado.web.Application([
(r"/", MainHandler),
])
application.listen(8888)
tornado.ioloop.IOLoop.current().start()
或者
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
define("port", default=8888, help="run on the given port", type=int)
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
def main():
tornado.options.parse_command_line()
application = tornado.web.Application([(r"/", MainHandler)])
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
if __name__ == "__main__":
main()
curl http://127.0.0.1:8888
curl -I http://127.0.0.1:8888
curl -s -w "%{time_total}\n" -o null http://127.0.0.1:8888
curl -H 'Cache-Control: no-cache' -I http://127.0.0.1:8888
一个简单的模板系统,将模板编译成 Python 代码。
<html>
<head>
<title>{{ title }}title>
head>
<body>
<div>爱看书的小沐的课程表div>
<ul>
{% for item in items %}
<li>{{ escape(item) }}li>
{% end %}
ul>
body>
html>
import tornado.ioloop
import tornado.web
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write('link to class 1' %
self.reverse_url("class", "1"))
items = ["星期一:体育课", "星期二:数学课", "星期三:语文课"]
self.render("templates\\test_tornado.html", title="My title", items=items)
class MyFormHandler(tornado.web.RequestHandler):
def get(self):
self.write('
''
''
'')
def post(self):
self.set_header("Content-Type", "text/plain")
self.write("You wrote " + self.get_body_argument("message"))
class ClassHandler(tornado.web.RequestHandler):
def initialize(self, db):
self.db = db
def get(self, class_id):
self.write("this is class %s" % db[min(int(class_id), len(db)-1)])
self.write(":
我们,这29个人,怀抱活力与梦想。前方有彩虹,前方有鲜花。")
db = ["zero", "one", "two"]
if __name__ == "__main__":
app = tornado.web.Application([
tornado.web.url(r"/", MainHandler),
tornado.web.url(r"/myform", MyFormHandler),
tornado.web.url(r"/class/([0-9]+)", ClassHandler, dict(db=db), name="class")
])
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
支持Tornado Web框架的wsgi。
wsgi是Web服务器的python标准,允许Tornado与其他python Web框架和服务器之间的互操作性。
WSGI是 同步的 接口,而Tornado的并发模型是基于单线程异步执行的。这意味着使用Tornado的 WSGIContainer 是 不可扩展 比在多线程的wsgi服务器上运行相同的应用程序 gunicorn 或 uwsgi . 使用 WSGIContainer 只有当Tornado和WSGi在同一个过程中结合起来的好处大于减少的可伸缩性时。
from tornado import ioloop
from tornado import wsgi
from tornado import httpserver
from tornado.web import Application
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
def simple_app(environ, start_response):
status = "200 OK"
response_headers = [("Content-type", "text/plain")]
start_response(status, response_headers)
return [b"Hello world, WSGI!\n"]
container = wsgi.WSGIContainer(simple_app)
http_server = httpserver.HTTPServer(container)
http_server.listen(8888)
ioloop.IOLoop.current().start()
WebSockets允许浏览器和服务器之间的双向通信。
所有主流浏览器的当前版本都支持 WebSockets,尽管不支持 WebSockets 的旧版本仍在使用(详情请参阅http://caniuse.com/websockets)。
此模块实现在中定义的WebSocket协议的最终版本。 RFC 6455 . 某些浏览器版本(特别是Safari 5.x)实现了早期的协议草案(称为“草案76”),与本模块不兼容。
from tornado import ioloop
from tornado.web import Application
from tornado.websocket import WebSocketHandler
import urllib
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
class EchoWebSocket(WebSocketHandler):
def open(self):
print("WebSocket opened")
def on_message(self, message):
print("message from client:" + message)
self.write_message(u"You said: " + message)
def on_close(self):
print("WebSocket closed")
# 这是针对浏览器上的跨站点脚本攻击的安全保护,
# 因为允许 WebSocket 绕过通常的同源策略并且不使用 CORS 标头。
# 允许所有跨域通讯,或允许来自您网站的任何子域的连接
def check_origin(self, origin):
parsed_origin = urllib.parse.urlparse(origin)
print(parsed_origin)
# return parsed_origin.netloc.endswith(".mydomain.com")
return True
def set_default_headers(self):
self.set_header('Access-Control-Allow-Origin', '*')
self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
self.set_header('Access-Control-Max-Age', 1000)
self.set_header('Access-Control-Allow-Headers', '*')
if __name__ == "__main__":
application = Application([
# (r"/", EchoWebSocket),
(r"/websocket", EchoWebSocket),
])
application.listen(8888)
ioloop.IOLoop.current().start()
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tornado Websocket Testtitle>
head>
<body>
<body onload='onLoad();'>
Message to send: <input type="text" id="msg"/>
<input type="button" onclick="sendMsg();" value="发送"/>
body>
body>
<script type="text/javascript">
var ws;
function onLoad() {
ws = new WebSocket("ws://localhost:8888/websocket");
ws.onopen = function() {
ws.send("Hello, world");
};
ws.onmessage = function (evt) {
//alert(evt.data);
console.log(evt.data)
};
}
function sendMsg() {
ws.send(document.getElementById('msg').value);
}
script>
html>
from tornado import ioloop
from tornado.web import Application
from tornado.websocket import WebSocketHandler
import urllib
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
clients = set()
class EchoWebSocket(WebSocketHandler):
def open(self):
print("WebSocket opened")
clients.add(self)
def on_message(self, message):
print("message from client:" + message)
self.write_message(u"You said: " + message)
for c in clients:
c.write_message(u"server said: " + message)
def on_close(self):
print("WebSocket closed")
clients.discard(self)
# 这是针对浏览器上的跨站点脚本攻击的安全保护,因为允许 WebSocket 绕过通常的同源策略并且不使用 CORS 标头。
# 允许所有跨域通讯,或允许来自您网站的任何子域的连接
def check_origin(self, origin):
parsed_origin = urllib.parse.urlparse(origin)
print(parsed_origin)
# return parsed_origin.netloc.endswith(".mydomain.com")
return True
def set_default_headers(self):
self.set_header('Access-Control-Allow-Origin', '*')
self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
self.set_header('Access-Control-Max-Age', 1000)
self.set_header('Access-Control-Allow-Headers', '*')
def ServerDogHandler():
for c in clients:
c.write_message(u"server said: a heartbeat meesage.")
if __name__ == "__main__":
application = Application([
# (r"/", EchoWebSocket),
(r"/websocket", EchoWebSocket),
])
application.listen(8888)
ioloop.PeriodicCallback(ServerDogHandler, 1000).start()
ioloop.IOLoop.current().start()
tornado.httpserver — Non-blocking HTTP server
一个非阻塞的单线程 HTTP 服务器。典型的应用程序与类几乎没有直接交互,HTTPServer 除了在进程开始时启动服务器(甚至通常通过 间接完成tornado.web.Application.listen)。
HTTPServer初始化遵循三种模式之一:
server = HTTPServer(app)
server.listen(8888)
IOLoop.current().start()
server = HTTPServer(app)
server.bind(8888)
server.start(0) # Forks multiple sub-processes
IOLoop.current().start()
sockets = tornado.netutil.bind_sockets(8888)
tornado.process.fork_processes(0)
server = HTTPServer(app)
server.add_sockets(sockets)
IOLoop.current().start()
tornado.httpclient — Asynchronous HTTP client
阻塞和非阻塞 HTTP 客户端接口。该模块定义了一个由两个实现共享的通用接口, simple_httpclient并且curl_httpclient. 应用程序可以直接实例化他们选择的实现类,也可以使用该模块中的AsyncHTTPClient类,该模块选择可以用该AsyncHTTPClient.configure方法覆盖的实现。默认实现是simple_httpclient,预计这将适合大多数用户的需求。
AsyncHTTPClient是一个异步非阻塞的HTTP客户端,使用AsyncHTTPClient方法需要提供回调函数callback。 HTTPClient是同步阻塞的HTTP客户端,它是完全同步的,也就是说当HTTPClient发生阻塞时,其他人是无法访问的。import tornado.httpclient
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.httpclient import HTTPClient, HTTPError
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
http_client = tornado.httpclient.HTTPClient()
try:
response = http_client.fetch("http://data.live.126.net/livechannel/classifylist.json")
print(response.body)
except tornado.httpclient.HTTPError as e:
# HTTPError is raised for non-200 responses; the response
# can be found in e.response.
print("Error: " + str(e))
except Exception as e:
# Other errors are possible, such as IOError.
print("Error: " + str(e))
http_client.close()
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from tornado.httpclient import AsyncHTTPClient
from tornado.ioloop import IOLoop
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
async def get_url():
http_client = AsyncHTTPClient()
try:
response = await http_client.fetch("http://img1.money.126.net/data/hs/time/today/1399001.json")
except Exception as e:
print("Error: %s" % e)
else:
print(response.body)
loop = asyncio.get_event_loop()
result = loop.run_until_complete(get_url())
loop.close()
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from tornado.httpclient import AsyncHTTPClient
from tornado.ioloop import IOLoop
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
def handle_request(response):
print(response)
if response.error:
print(response.error)
else:
print(response.body)
client = AsyncHTTPClient()
client.fetch("http://img1.money.126.net/data/hs/time/today/1399001.json", handle_request)
IOLoop.instance().start()
# Fetch the url and print its body
python -m tornado.httpclient http://www.baidu.com
# Just print the headers
python -m tornado.httpclient --print_headers --print_body=false http://www.baidu.com
Tornado有了tornado.ioloop和tornado.iostream两个模块的帮助可以实现异步Web服务器,tornado.httpserver是Tornado的Web服务器模块,该模块中实现了HTTPServer - 一个单线程HTTP服务器,其实现是基于tornado.tcpserver模块的TCPServer。
TCPServer是一个非阻塞单线程的TCP服务器,负责处理TCP协议部分的内容,并预留handle_stream抽象接口方法针对相应的应用层协议编写服务器。
(1)使用bind + start绑定开启的方式用于多进程
import tornado.tcpserver from TCPServer
import tornado.ioloop from IOLoop
server = TCPServer()
server.listen(8000)
IOLoop.current().start()
(2)使用listen监听的方式用于单进程
import tornado.tcpserver from TCPServer
import tornado.ioloop from IOLoop
server = TCPServer()
server.bind(8000)
server.starat(0) # fork派生创建多个子进程
IOLoop.current().start()
#! /usr/bin/env python3
# -*- coding=utf-8 -*-
from tornado.tcpserver import TCPServer
from tornado.ioloop import IOLoop
from tornado.options import define, options
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
define("port", type=int, default=8888)
class Server(TCPServer):
def handle_stream(self, sockfd, client_addr):
print("client: ", client_addr)
sockfd.read_until_close(self.handle_recv)
def handle_recv(self, data):
print(data)
if __name__=="__main__":
options.parse_command_line()
server = Server()
server.listen(options.port, address="127.0.0.1")
IOLoop.instance().start()
#! /usr/bin/env python3
# -*- coding=utf-8 -*-
from tornado.tcpserver import TCPServer
from tornado.ioloop import IOLoop
from tornado.options import define, options
from tornado import iostream, gen
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
class Server(TCPServer):
@gen.coroutine
def handle_stream(self, stream, address):
try:
while True:
msg = yield stream.read_bytes(128, partial = True)
print(msg, "from", address)
yield stream.write(msg[::-1])
if msg == "quit":
stream.close()
except iostream.StreamClosedError:
pass
if __name__ == "__main__":
server = Server()
server.listen(8888)
server.start()
IOLoop.current().start()
import asyncio
import errno
import functools
import socket
import tornado.ioloop
from tornado.iostream import IOStream
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
async def handle_connection(connection, address):
print("handle_connection", address)
stream = IOStream(connection)
message = await stream.read_until_close()
print("message from client:", message.decode().strip())
def connection_ready(sock, fd, events):
print("connection_ready")
while True:
try:
connection, address = sock.accept()
except BlockingIOError:
return
connection.setblocking(0)
io_loop = tornado.ioloop.IOLoop.current()
io_loop.spawn_callback(handle_connection, connection, address)
print("while:", address)
async def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setblocking(0)
sock.bind(("", 8888))
sock.listen(128)
io_loop = tornado.ioloop.IOLoop.current()
callback = functools.partial(connection_ready, sock)
io_loop.add_handler(sock.fileno(), callback, io_loop.READ)
await asyncio.Event().wait()
if __name__ == "__main__":
asyncio.run(main())
#! /usr/bin/python3
# encoding=utf-8
from tornado import gen, iostream
from tornado.tcpclient import TCPClient
from tornado.ioloop import IOLoop
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
@gen.coroutine
def Trans():
stream = yield TCPClient().connect("127.0.0.1", 8888)
try:
while True:
data = input("Enter: ")
yield stream.write( str(data).encode('utf-8') )
back = yield stream.read_bytes(20, partial = True)
msg = yield stream.read_bytes(20, partial = True)
print(back, msg)
if data == "quit":
break
except iostream.StreamClosedError:
pass
if __name__ == "__main__":
IOLoop.current().run_sync(Trans)
要使此服务器为 SSL 流量提供服务,请发送ssl_options带有ssl.SSLContext对象的关键字参数。为了与旧版本的 Python 兼容,ssl_options还可能是该ssl.wrap_socket方法的关键字参数字典:
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_ctx.load_cert_chain(os.path.join(data_dir, "mydomain.crt"),
os.path.join(data_dir, "mydomain.key"))
HTTPServer(application, ssl_options=ssl_ctx)
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import ssl
from tornado.httpserver import HTTPServer
from tornado.web import Application, RequestHandler
from tornado.ioloop import IOLoop
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
class TestHandler(RequestHandler):
def get(self):
self.write("Hello, World!\n")
settings = {
"static_path" : os.path.join(os.path.dirname(__file__), "static"),
}
application = Application([
(r"/", TestHandler),
], **settings)
if __name__ == "__main__":
server = HTTPServer(application,ssl_options={
"certfile": os.path.join(os.path.abspath("."), "server.crt"),
"keyfile": os.path.join(os.path.abspath("."), "server.key"),
})
server.listen(8000)
IOLoop.instance().start()
windows下建议采用nginx + tornado方案。
使用nginx web服务器,tornado充当wsgi,tornado负责监听5000端口。
# -*- coding: utf-8 -*-
import tornado.ioloop
import tornado.web
import tornado.options
from tornado.options import options, define, parse_command_line
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
define('port', type=int, default=8080)
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello World,爱看书的小沐,2022!" + "
port: " + options.port)
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application([(r"/", MainHandler)])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
worker_processes 1;
events {
worker_connections 1024;
}
http {
upstream frontends {
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
server 127.0.0.1:8003;
}
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location ^~ /static/ {
root D:\PythonProject\django_web;
if ($query_string) {
expires max;
}
}
location ^~ /media/ {
root D:\PythonProject\django_web;
if ($query_string) {
expires max;
}
}
location = /favicon.ico {
rewrite (.*) /static/favicon.ico;
}
location = /robots.txt {
rewrite (.*) /static/robots.txt;
}
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://frontends;
}
}
}
python test_tornado.py --port=8000
python test_tornado.py --port=8001
python test_tornado.py --port=8002
python test_tornado.py --port=8003
cd ..
cd ..
cd nginx
start nginx
nginx.exe -s quit #停止服务
nginx.exe -s stop #停止服务
nginx.exe -s reload #重启服务
运行结果如下:
torndb数据库简介 在Tornado3.0版本以前提供tornado.database模块用来操作MySQL数据库,而从3.0版本开始,此模块就被独立出来,作为torndb包单独提供。 torndb只是对MySQLdb的简单封装,不支持Python 3。
MySQLdb不支持python3.x
PyMySQL支持python3.x
这里使用PyMySQL进行测试:
pip install PyMySQL
#!/usr/bin/env Python
# coding=utf-8
import tornado.ioloop
import tornado.web
import tornado.options
from tornado.options import options, define, parse_command_line
import pymysql
import platform
if platform.system() == "Windows":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
define('port', type=int, default=8888)
settings = {
"template_path":"templates",
"static_path":"statics",
}
class LoginHandler(tornado.web.RequestHandler):
# def get(self):
# self.render('login.html')
def get(self):
self.write('
'用户名称:
'
'用户密码:
'
''
'')
def post(self, *args, **kwargs):
username = self.get_argument('username',None)
password = self.get_argument('password', None)
# 数据库连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='data_platform')
cursor = conn.cursor()
sql = "select username from t_user where username='%s' and password='%s'" %(username, password)
effect_row = cursor.execute(sql)
result = 0
if effect_row > 0:
result = cursor.fetchone()
conn.commit()
cursor.close()
conn.close()
print(effect_row, result)
if result:
# self.render('index.html', items=['登录成功!', result[0]])
self.write('爱看书的小沐(%s),恭喜登陆成功!' % username)
else:
# self.render('index.html', items=['登录失败!'])
self.write('爱看书的小沐(%s),很遗憾登陆失败。' % username)
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello World,爱看书的小沐(tornado),2022!" + "
port: " + str(options.port))
def make_app():
return tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
],**settings)
if __name__ == "__main__":
tornado.options.parse_command_line()
app = make_app()
server = tornado.httpserver.HTTPServer(app)
server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
如果您觉得该方法或代码有一点点用处,可以给作者点个赞,或打赏杯咖啡;
╮( ̄▽ ̄)╭
如果您感觉方法或代码不咋地//(ㄒoㄒ)//,就在评论处留言,作者继续改进;
o_O???
如果您需要相关功能的代码定制化开发,可以留言私信作者;
(✿◡‿◡)
感谢各位童鞋们的支持!
( ´ ▽´ )ノ ( ´ ▽´)っ!!!