1. 程式人生 > >Python Tornado篇

Python Tornado篇

req 添加 log coo from proto server bind key

Tornado既是一個web server,也是web framework。而它作為web server 采用的是asynchronous IO的網絡模型,這是一種很高效的模型。

Tornado 和現在的主流 Web 服務器框架(包括大多數 Python 的框架)有著明顯的區別:它是非阻塞式服務器,而且速度相當快。得利於其 非阻塞的方式和對 epoll 的運用,Tornado 每秒可以處理數以千計的連接,這意味著對於實時 Web 服務來說,Tornado 是一個理想的 Web 框架。

同步IO操作導致請求進程阻塞,知道IO操作完成;異步IO操作不導致請求進程阻塞。

在Python中,同步IO可以被李傑為一個被調用的IO函數會阻塞調用函數的執行,而異步IO則不會阻塞調用函數執行。

安裝pip3 install tornado

tornado網站架構簡單示例:

技術分享圖片
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        import time
        time.sleep(10)
        self.write("Hello, world")

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.write(
"Index") application = tornado.web.Application([ (r"/main", MainHandler), (r"/index", IndexHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
View Code

執行過程:

  • 第一步:執行腳本,監聽 8888 端口
  • 第二步:瀏覽器客戶端訪問 /index --> http://127.0.0.1:8888/index
  • 第三步:服務器接受請求,並交由對應的類處理該請求
  • 第四步:類接受到請求之後,根據請求方式(post / get / delete ...)的不同調用並執行相應的方法
  • 第五步:方法返回值的字符串內容發送瀏覽器
#__author:  Administrator
#date:  2017/3/10
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

class LoginHandler(tornado.web.RequestHandler):
    def get(self):
        # 5.獲取用戶請求相關信息
        # self.get_cookie()
        # v = self.get_argument(‘p‘)
        # print(v)
        # self.request 封裝了用戶發來的所有請求
        # print(type(self.request))
        # from tornado.httputil import HTTPServerRequest

        # 6. 額外相應內容
        # self.set_cookie(‘k1‘,‘v1‘)
        # self.set_header(‘h1‘,‘v1‘)

        # 4. 返回頁面+模版引擎
        # self.render(‘login.html‘)
        # self.render(‘login.html‘,k1=‘v1‘)
        # self.render(‘login.html‘,k1=‘v1‘,k2=‘v2‘)
        self.render(login.html,**{k1:v1,k2:[1,2,3,4],k3:{name:root,age:18}})
        # 7. 重定向
        # self.redirect(‘/index/‘)

    def post(self, *args, **kwargs):
        v = self.get_argument(user)
        print(v)
        self.redirect(http://www.autohome.com.cn)
import tor.uimethods as mt
from tor import uimodules as md

# 8. 配置
settings = {
    static_path: static,
    static_url_prefix: /sss/,
    template_path:templates,
    ui_methods: mt,
    ui_modules: md,
}
# 1.生成路由規則
application = tornado.web.Application([(r"/index", MainHandler),(r"/login", LoginHandler),],**settings)

if __name__ == "__main__":
    # 2. 創建socket對象8888
    # 將socket對象添加到select或epoll中
    application.listen(8888)
    # 3. 將select或epoll開始死循環 While True:
    tornado.ioloop.IOLoop.instance().start()

異步非阻塞兩種做法:

技術分享圖片
import tornado.ioloop
import tornado.web
from tornado import gen

class MainHandler(tornado.web.RequestHandler):
    @gen.coroutine
    def get(self):
        from tornado import httpclient
        http = httpclient.AsyncHTTPClient()
        yield http.fetch("http://www.google.com", self.done)

    def done(self, *args, **kwargs):
        self.write(Main)
        self.finish()

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Index")

application = tornado.web.Application([
    (r"/main", MainHandler),
    (r"/index", IndexHandler),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
使用AsyncHTTPClient

httpclient.AsyncHTTPClient()是異步訪問,fetch函數會在調用後立即返回而不用等待實際訪問的完成,從而導致get()也會立刻執行完成。

當訪問實際完成後,AsyncHTTPClient會調用callback參數指定的函數,這裏可以任意寫代碼。

裝飾器 + Future 從而實現Tornado的異步非阻塞

技術分享圖片
import tornado.ioloop
import tornado.web
from tornado import gen
from tornado.concurrent import Future

future = None
class MainHandler(tornado.web.RequestHandler):
    @gen.coroutine
    def get(self):
        global future
        future = Future()
        future.add_done_callback(self.done)
        yield future

    def done(self, *args, **kwargs):
        self.write(Main)
        self.finish()

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        global future
        future.set_result(None)
        self.write("Index")

application = tornado.web.Application([
    (r"/main", MainHandler),
    (r"/index", IndexHandler),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
使用Future

當發送GET請求時,由於方法被@gen.coroutine裝飾且yield 一個 Future對象,那麽Tornado會等待,等待用戶向future對象中放置數據或者發送信號,如果獲取到數據或信號之後,就開始執行done方法。

異步非阻塞體現在當在Tornaod等待用戶向future對象中放置數據時,還可以處理其他請求。

註意:在等待用戶向future對象中放置數據或信號時,此連接是不斷開的。

自定義異步非阻塞框架

import socket
import select

class HttpRequest(object):
    """
    用戶封裝用戶請求信息
    """
    def __init__(self, content):
        """
        :param content:用戶發送的請求數據:請求頭和請求體
        """
        self.content = content
        self.header_bytes = bytes()
        self.body_bytes = bytes()
        self.header_dict = {}
        self.method = ""
        self.url = ""
        self.protocol = ""
        self.initialize()
        self.initialize_headers()

    def initialize(self):
        temp = self.content.split(b\r\n\r\n, 1)
        if len(temp) == 1:
            self.header_bytes += temp
        else:
            h, b = temp
            self.header_bytes += h
            self.body_bytes += b

    @property
    def header_str(self):
        return str(self.header_bytes, encoding=utf-8)

    def initialize_headers(self):
        headers = self.header_str.split(\r\n)
        first_line = headers[0].split( )
        if len(first_line) == 3:
            self.method, self.url, self.protocol = headers[0].split( )
            for line in headers:
                kv = line.split(:)
                if len(kv) == 2:
                    k, v = kv
                    self.header_dict[k] = v

class Future(object):
    def __init__(self):
        self.result = None

F = None

def main(request):
    global F
    F = Future()
    return F

def stop(request):
    global F
    F.result = b"xxxxxxxxxxxxx"
    return "stop"

def index(request):
    return "indexasdfasdfasdf"

routers = [
    (/main/,main),
    (/index/,index),
    (/stop/,stop),
]

def run():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(("127.0.0.1", 9999,))
    sock.setblocking(False)
    sock.listen(128)
    inputs = []
    inputs.append(sock)
    async_request_dict = {
        # ‘socket‘: futrue
    }

    while True:
        rlist,wlist,elist = select.select(inputs,[],[],0.05)
        for r in rlist:
            if r == sock:
                """新請求到來"""
                conn,addr = sock.accept()
                conn.setblocking(False)
                inputs.append(conn)
            else:
                """客戶端發來數據"""
                data = b""
                while True:
                    try:
                        chunk = r.recv(1024)
                        data = data + chunk
                    except Exception as e:
                        chunk = None
                    if not chunk:
                        break
                # data進行處理:請求頭和請求體
                request = HttpRequest(data)
                print(request.url)
                print(request.method)
                print(request.body_bytes)
                # 1. 請求頭中獲取url
                # 2. 去路由中匹配,獲取指定的函數
                # 3. 執行函數,獲取返回值
                # 4. 將返回值 r.sendall(b‘alskdjalksdjf;asfd‘)
                import re
                flag = False
                func = None
                for route in routers:
                    if re.match(route[0],request.url):
                        flag = True
                        func = route[1]
                        break
                if flag:
                    result = func(request)
                    if isinstance(result,Future):
                        async_request_dict[r] = result
                    else:
                        r.sendall(bytes(result,encoding=utf-8))
                        inputs.remove(r)
                        r.close()
                else:
                    r.sendall(b"404")
                    inputs.remove(r)
                    r.close()

        for conn in async_request_dict.keys():
            future = async_request_dict[conn]
            if future.result:
                conn.sendall(future.result)
                conn.close()
                del async_request_dict[conn]
                inputs.remove(conn)

if __name__ == __main__:
    run()

Python Tornado篇