Python Tornado篇
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(View Code"Index") application = tornado.web.Application([ (r"/main", MainHandler), (r"/index", IndexHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
執行過程:
- 第一步:執行腳本,監聽 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篇