【小沐学Python】Python实现Web服务器(Tornado,HTTP/TCP/Websocket)

66 篇文章 12 订阅

🍺Web服务器系列相关文章编写如下🍺:

  1. 🎈【Web开发】Node.js实现Web服务器(http模块)🎈
  2. 🎈【Web开发】Node.js实现Web服务器(express模块)🎈
  3. 🎈【Web开发】Python实现Web服务器(Flask入门)🎈
  4. 🎈【Web开发】Python实现Web服务器(Flask测试)🎈
  5. 🎈【Web开发】Python实现Web服务器(Tornado入门)🎈
  6. 🎈【Web开发】Python实现Web服务器(Tornado+flask+nginx)🎈
  7. 🎈【Web开发】Python实现Web服务器(FastAPI)🎈
  8. 🎈【Web开发】Android手机上基于Termux实现Web服务器(Python、node.js)🎈

1、简介

在这里插入图片描述

1.1 什么是Tornado?

Tornado龙卷风是一个开源的网络服务器框架(一个python web框架和异步网络库),它是基于社交聚合网站FriendFeed的实时信息服务开发而来的。2007年由4名Google前软件工程师一起创办了FriendFeed,旨在使用户能够方便地跟踪好友在Facebook和Twitter等多个社交网站上的活动。

  • Tornado通过使用非阻塞网络I/O,Tornado可以扩展到数万个开放连接,使其非常适合 long polling , WebSockets 以及其他需要与每个用户建立长期连接的应用程序。
  • Tornado与现代主流的Web服务器框架有着明显的区别:它使非阻塞式的服务器,速度相当快。这得益于其非阻塞的方式和对epoll的运用。Tornado每秒可以处理数以千计的连接。
  • Tornado是实时Web服务的一个理想框架,它非常适合开发长轮询、WebSocket和需要与每个用户建立持久连接的应用。
  • 与Node.js相同的是,Tornado也采用的是单进程单线程异步IO的网络模型,它们都可以编写异步非阻塞的程序。

1.2 Tornado的功能模块

Tornado的主要功能模块:

  • (1)Web framework(Web框架)
    tornado.web、tornado.template、tornado.routing、tornado.escape、tornado.locale、tornado.websocket等。
  • (2)HTTP servers and clients(HTTP 服务器和客户端)
    tornado.httpserver、tornado.httpclient、tornado.httputil、tornado.http1connection等。
  • (3)Asynchronous networking(异步网络)
    tornado.ioloop、tornado.iostream、tornado.netutil、tornado.tcpclient、tornado.tcpserver等。
  • (4)Coroutines and concurrency(协程和并发)
    tornado.gen 、tornado.locks、tornado.queues、tornado.process等。
  • (5)Integration with other services(与其他服务集成)
    tornado.auth、tornado.wsgi、tornado.platform.caresresolver、tornado.platform.twisted、tornado.platform.asyncio等。
  • (6)Utilities (实用程序)
    tornado.autoreload、tornado.concurrent、tornado.log、tornado.options、tornado.testing、tornado.util等。

其中Tornado的服务器三个底层核心模块:

  • (1)httpserver 服务于web模块的一个简单的HTTP服务器的实现
  • (2)iostream 对非阻塞式的socket的封装以便于常见读写操作
  • (3)ioloop 核心的I/O循环

2、安装

https://github.com/tornadoweb/tornado
https://www.tornadoweb.org/en/stable/

Tornado与标准库集成 asyncio 模块和共享相同的事件循环(默认情况下,从Tornado 5.0开始)。通常,设计用于 asyncio 可以与 Tornado 自由混合。

  • 安装最新版本执行如下命令:
pip install tornado
  • 运行结果:
    在这里插入图片描述
  • 安装版本5.1执行如下命令:
pip install tornado==5.1.1

在这里插入图片描述

3、入门示例

3.1 tornado.web

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()
  • 服务端运行结果:

在这里插入图片描述

  • 测试客户端运行结果:
  • 1.发送get请求
curl http://127.0.0.1:8888

在这里插入图片描述

  • 2.检查响应头
curl -I http://127.0.0.1:8888

在这里插入图片描述

  • 3.显示回应时间
curl -s -w "%{time_total}\n" -o null http://127.0.0.1:8888

在这里插入图片描述

  • 4.发送请求头
curl -H 'Cache-Control: no-cache' -I http://127.0.0.1:8888

在这里插入图片描述

3.2 tornado.template

一个简单的模板系统,将模板编译成 Python 代码。

  • test_tornado.html:
<html>
   <head>
      <title>{{ title }}</title>
   </head>
   <body>
    <div>爱看书的小沐的课程表</div>
     <ul>
       {% for item in items %}
         <li>{{ escape(item) }}</li>
       {% end %}
     </ul>
   </body>
 </html>
  • test_tornado.py:
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('<a href="%s">link to class 1</a>' %
                   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('<html><body><form action="/myform" method="POST">'
                   '<input type="text" name="message">'
                   '<input type="submit" value="Submit">'
                   '</form></body></html>')

    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(":<br>我们,这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()
  • 运行结果:
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3.3 tornado.wsgi

支持Tornado Web框架的wsgi。
wsgi是Web服务器的python标准,允许Tornado与其他python Web框架和服务器之间的互操作性。

WSGI是 同步的 接口,而Tornado的并发模型是基于单线程异步执行的。这意味着使用Tornado的 WSGIContainer 是 不可扩展 比在多线程的wsgi服务器上运行相同的应用程序 gunicorn 或 uwsgi . 使用 WSGIContainer 只有当Tornado和WSGi在同一个过程中结合起来的好处大于减少的可伸缩性时。

  • test_wsgi.py
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()
  • 运行结果:
    在这里插入图片描述

3.4 tornado.websocket

WebSockets允许浏览器和服务器之间的双向通信。

所有主流浏览器的当前版本都支持 WebSockets,尽管不支持 WebSockets 的旧版本仍在使用(详情请参阅http://caniuse.com/websockets)。

此模块实现在中定义的WebSocket协议的最终版本。 RFC 6455 . 某些浏览器版本(特别是Safari 5.x)实现了早期的协议草案(称为“草案76”),与本模块不兼容。

  • test_websocket.py(一对一方式)
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()
  • test_websocket.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Tornado Websocket Test</title>
</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>
  • 运行结果:
    在这里插入图片描述
    在这里插入图片描述
    • test_websocket_mutil.py(一对多方式)
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()
  • 运行结果:
    在这里插入图片描述
    在这里插入图片描述

3.5 tornado.httpserver

tornado.httpserver — Non-blocking HTTP server

一个非阻塞的单线程 HTTP 服务器。典型的应用程序与类几乎没有直接交互,HTTPServer 除了在进程开始时启动服务器(甚至通常通过 间接完成tornado.web.Application.listen)。

HTTPServer初始化遵循三种模式之一:

  1. listen:简单的单进程:
    在很多情况下,tornado.web.Application.listen可以用来避免显式创建HTTPServer.
server = HTTPServer(app)
server.listen(8888)
IOLoop.current().start()
  1. bind/ start:简单的多进程:
    使用此接口时,不得将 anIOLoop传递给构造HTTPServer函数。 start将始终在默认单例上启动服务器IOLoop。
server = HTTPServer(app)
server.bind(8888)
server.start(0)  # Forks multiple sub-processes
IOLoop.current().start()
  1. add_sockets:高级多进程:
    接口更复杂,add_sockets但可以使用 withtornado.process.fork_processes为您在分叉发生时提供更大的灵活性。 add_sockets如果您想以除tornado.netutil.bind_sockets.
sockets = tornado.netutil.bind_sockets(8888)
tornado.process.fork_processes(0)
server = HTTPServer(app)
server.add_sockets(sockets)
IOLoop.current().start()

3.6 tornado.httpclient

tornado.httpclient — Asynchronous HTTP client

阻塞和非阻塞 HTTP 客户端接口。该模块定义了一个由两个实现共享的通用接口, simple_httpclient并且curl_httpclient. 应用程序可以直接实例化他们选择的实现类,也可以使用该模块中的AsyncHTTPClient类,该模块选择可以用该AsyncHTTPClient.configure方法覆盖的实现。默认实现是simple_httpclient,预计这将适合大多数用户的需求。

AsyncHTTPClient是一个异步非阻塞的HTTP客户端,使用AsyncHTTPClient方法需要提供回调函数callback。 HTTPClient是同步阻塞的HTTP客户端,它是完全同步的,也就是说当HTTPClient发生阻塞时,其他人是无法访问的。
  1. 一个阻塞的 HTTP 客户端。
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()
  • 运行结果如下:
    在这里插入图片描述
  1. 非阻塞 HTTP 客户端。
#!/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()
  • 运行结果如下:
    在这里插入图片描述
  1. 命令行界面
# 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

3.7 tornado.tcpserver

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()
  • TCPServer测试例子1:
#! /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()
  • TCPServer测试例子2:
#! /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()
  • TCPServer测试例子3:
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())
  • 运行结果(使用c++客户端连接TornadoTCP服务端):
    (1)TornadoTCP服务端
    在这里插入图片描述
    (2)c++客户端
    在这里插入图片描述

3.8 tornado.tcpclient

#! /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)
  • 运行结果(使用c++服务端连接TornadoTCP客户端):
    (1)TornadoTCP客户端
    在这里插入图片描述
    (2)c++服务端
    在这里插入图片描述

4、扩展示例

4.1 tornado+https

要使此服务器为 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()

4.2 tornado+nginx

windows下建议采用nginx + tornado方案。
使用nginx web服务器,tornado充当wsgi,tornado负责监听5000端口。

  • 编写脚本test_tornado.py:
# -*- 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!" + "<br>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()
  • 修改配置文件nginx.conf:
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;
        }
    }
}
  • (1)运行tornado服务
python test_tornado.py --port=8000
python test_tornado.py --port=8001
python test_tornado.py --port=8002
python test_tornado.py --port=8003
  • (2)运行nginx.exe启动程序
cd ..
cd ..
cd nginx
start nginx
nginx.exe -s quit       #停止服务
nginx.exe -s stop       #停止服务
nginx.exe -s reload     #重启服务
  • (3)浏览器访问http://127.0.0.1
    启动后在浏览器中输入http://127.0.0.1或http://localhost后,即显示nginx欢迎画面。

运行结果如下:

在这里插入图片描述

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

4.3 tornado+mysql

torndb数据库简介 在Tornado3.0版本以前提供tornado.database模块用来操作MySQL数据库,而从3.0版本开始,此模块就被独立出来,作为torndb包单独提供。 torndb只是对MySQLdb的简单封装,不支持Python 3。

MySQLdb不支持python3.x
PyMySQL支持python3.x

这里使用PyMySQL进行测试:

pip install PyMySQL
  • Python测试代码如下:
#!/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('<html><body><form action="/login" method="POST">'
                '用户名称:<input type="text" name="username"><br>'
                '用户密码:<input type="text" name="password"><br>'
                '<input type="submit" value="登陆">'
                '</form></body></html>')

    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('<html><body>爱看书的小沐(%s),恭喜登陆成功!</body></html>' % username)
        else:
            # self.render('index.html', items=['登录失败!'])
            self.write('<html><body>爱看书的小沐(%s),很遗憾登陆失败。</body></html>' % username)
        
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello World,爱看书的小沐(tornado),2022!" + "<br>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???
如果您需要相关功能的代码定制化开发,可以留言私信作者;(✿◡‿◡)
感谢各位童鞋们的支持!( ´ ▽´ )ノ ( ´ ▽´)っ!!!

在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值