異步非阻塞
阻塞式:(適用於所有框架,Django,Flask,Tornado,Bottle)
一個請求到來未處理完成,后續一直等待
解決方案:多線程,多進程
異步非阻塞(存在IO請求): Tornado(單進程+單線程)
使用異步非阻塞,需要遵循Tornado框架內部規則,gen
多個連接請求,連接給服務端,如果是有異步非阻塞的話,服務端會接收所有的請求交由后台處理,等待其他鏈接的同時,原先連接不斷開,直至返回后台處理完成的結果!
外部請求,連接服務端 或在select中創建Future對象,然后服務端再把請求交給業務處理平台,此時select監聽的列表中又會生成一個socket對象,當業務平台對請求處理完成之后就會把信息返回到服務端的select監聽列表中,同時對這個Future對象賦值,用於標記服務端是否要給客戶端返回請求信息。
執行流程,本質上都是返回一個future對象,如果對這個對象被set_result了就返回值,否則就是夯住,一直保持連接,不終止請求。
1、基本使用
裝飾器 + Future 從而實現Tornado的異步非阻塞
class AsyncHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
future = Future()
future.add_done_callback(self.doing)
yield future
# 或
# tornado.ioloop.IOLoop.current().add_future(future,self.doing)
# yield future
def doing(self,*args, **kwargs):
self.write('async')
self.finish()
當發送GET請求時,由於方法被@gen.coroutine裝飾且yield 一個 Future對象,那么Tornado會等待,等待用戶向future對象中放置數據或者發送信號,如果獲取到數據或信號之后,就開始執行doing方法。
異步非阻塞體現在當在Tornaod等待用戶向future對象中放置數據時,還可以處理其他請求。
注意:在等待用戶向future對象中放置數據或信號時,此連接是不斷開的。
2、同步阻塞和異步非阻塞對比
class SyncHandler(tornado.web.RequestHandler): def get(self): self.doing() self.write('sync') def doing(self): time.sleep(10)
class AsyncHandler(tornado.web.RequestHandler): @gen.coroutine def get(self): future = Future() tornado.ioloop.IOLoop.current().add_timeout(time.time() + 5, self.doing) yield future def doing(self, *args, **kwargs): self.write('async') self.finish()
3、httpclient類庫
Tornado提供了httpclient類庫用於發送Http請求,其配合Tornado的異步非阻塞使用。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.web
from tornado import gen
from tornado import httpclient
# 方式一:
class AsyncHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self, *args, **kwargs):
print('進入')
http = httpclient.AsyncHTTPClient()
data = yield http.fetch("http://www.google.com")
print('完事',data)
self.finish('6666')
# 方式二:
# class AsyncHandler(tornado.web.RequestHandler):
# @gen.coroutine
# def get(self):
# print('進入')
# http = httpclient.AsyncHTTPClient()
# yield http.fetch("http://www.google.com", self.done)
#
# def done(self, response):
# print('完事')
# self.finish('666')
application = tornado.web.Application([
(r"/async", AsyncHandler),
])
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
#!/usr/bin/env python # -*- coding:utf-8 -*- """ 需要先安裝支持異步操作Mysql的類庫: Tornado-MySQL: https://github.com/PyMySQL/Tornado-MySQL#installation pip3 install Tornado-MySQL """ import tornado.web from tornado import gen import tornado_mysql from tornado_mysql import pools POOL = pools.Pool( dict(host='127.0.0.1', port=3306, user='root', passwd='123', db='cmdb'), max_idle_connections=1, max_recycle_sec=3) @gen.coroutine def get_user_by_conn_pool(user): cur = yield POOL.execute("SELECT SLEEP(%s)", (user,)) row = cur.fetchone() raise gen.Return(row) @gen.coroutine def get_user(user): conn = yield tornado_mysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='cmdb', charset='utf8') cur = conn.cursor() # yield cur.execute("SELECT name,email FROM web_models_userprofile where name=%s", (user,)) yield cur.execute("select sleep(10)") row = cur.fetchone() cur.close() conn.close() raise gen.Return(row) class LoginHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): self.render('login.html') @gen.coroutine def post(self, *args, **kwargs): user = self.get_argument('user') data = yield gen.Task(get_user, user) if data: print(data) self.redirect('http://www.oldboyedu.com') else: self.render('login.html') application = tornado.web.Application([ (r"/login", LoginHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
異步非阻塞漸進學習代碼+筆記注釋
import tornado.ioloop import tornado.web from tornado.web import RequestHandler from tornado.httpserver import HTTPServer # 單線程操作,請求來排隊等待,順序執行 #人為干預模擬IO設置 sleep10秒 class IndexHandler(RequestHandler): def get(self): print('開始') import time time.sleep(10) self.write("Hello, world") print('結束') application = tornado.web.Application([ (r"/index", IndexHandler), ]) if __name__ == "__main__": # 單進程單線程 application.listen(8888) tornado.ioloop.IOLoop.instance().start() # 利用多進程 實現 # server = HTTPServer(application) # server.bind(8888) # server.start(4) # Forks multiple sub-processes # tornado.ioloop.IOLoop.current().start()
import tornado.ioloop import tornado.web from tornado.web import RequestHandler from tornado import gen # 執行異步IO 導入gen模塊 from tornado.concurrent import Future # 執行異步IO導入 Future模塊,引用Future對象 import time # 單線程 實現異步非阻塞操作!所有的連接請求不等待直接執行 class IndexHandler(RequestHandler): @gen.coroutine # 異步IO 固定寫法,在請求上以裝飾器的形式添加 def get(self): print('開始') future = Future() #創建 Future() 對象 tornado.ioloop.IOLoop.current().add_timeout(time.time() + 10, self.doing) #給當前的客戶端添加超時時間,固定寫法 yield future # yield 返回 Future() 對象 IO操作的固定寫法 # 操作完成之后,需要執行的回調函數,一般是用於給請求返回消息 def doing(self, *args, **kwargs): self.write('async') # 返回消息 self.finish() #結束連接 application = tornado.web.Application([ (r"/index", IndexHandler), ]) if __name__ == "__main__": # 單線程 application.listen(8888) tornado.ioloop.IOLoop.instance().start() # 多進程 # server = HTTPServer(application) # server.bind(8888) # server.start(4) # Forks multiple sub-processes # tornado.ioloop.IOLoop.current().start()
import tornado.ioloop import tornado.web from tornado.web import RequestHandler from tornado import gen from tornado.concurrent import Future import time from tornado import httpclient #針對HTTP請求進行異步非阻塞處理的模塊 # 針對API接口 HTTP請求 實現異步非阻塞 class IndexHandler(RequestHandler): @gen.coroutine def get(self): print('收到訂單') http = httpclient.AsyncHTTPClient() #創建 執行異步非阻塞 客戶端 yield http.fetch("http://www.github.com", self.done) # 固定寫法 請求對某個API接口(url地址)傳遞消息,處理完畢執行回調函數 #請求處理完畢,執行的回調函數。 def done(self, response): self.write('訂單成功') # 給請求返回的信息 self.finish() # 斷開連接 application = tornado.web.Application([ (r"/index", IndexHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start() # server = HTTPServer(application) # server.bind(8888) # server.start(4) # Forks multiple sub-processes # tornado.ioloop.IOLoop.current().start()
import tornado.ioloop import tornado.web from tornado.web import RequestHandler from tornado import gen from tornado.concurrent import Future import time from tornado import httpclient fu = None class IndexHandler(RequestHandler): @gen.coroutine def get(self): global fu print('瘋狂的追求') fu = Future() # 創建Future對象,建立連接,如果沒有人改變狀態,請求就會永久存在連接不斷開,除非Future() 對象被賦值或是反生改變 # fu.set_result("") # 給Future 對象賦值,fu發生變化,返回請求。 fu.add_done_callback(self.done)# 給fu添加要執行的回調函數 yield fu def done(self, response): self.write('終於等到你') self.finish() class TestHandler(RequestHandler): def get(self): fu.set_result(666) # 給future對象賦值,用以改變連接狀態,返回消息 (注意:返回的內容就是 result的值) self.write('我只能幫你到這里了') application = tornado.web.Application([ (r"/index", IndexHandler), (r"/test", TestHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
import tornado.ioloop import tornado.web from tornado.web import RequestHandler from tornado import gen from tornado.concurrent import Future import time from tornado import httpclient from threading import Thread def waiting(futher): #線程要執行處理的函數 import time time.sleep(10) futher.set_result(666) class IndexHandler(RequestHandler): @gen.coroutine def get(self): global fu print('瘋狂的追求') fu = Future() fu.add_done_callback(self.done) thread = Thread(target=waiting,args=(fu,)) # 開一個線程 自動給設置值,以自動給請求返回處理的消息 thread.start() yield fu def done(self, response): self.write('終於等到你') self.finish() application = tornado.web.Application([ (r"/index", IndexHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
import tornado.web import tornado.ioloop from tornado import gen import tornado_mysql @gen.coroutine # 注意需要寫上裝飾器 def get_user(user): # 異步非阻塞,Task操作的函數,連接數據庫,注意語法結構 conn = yield tornado_mysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='tornadoTest', charset='utf8') cur = conn.cursor() # yield cur.execute("SELECT name,email FROM web_models_userprofile where name=%s", (user,)) yield cur.execute("select sleep(10)") row = cur.fetchone() cur.close() conn.close() raise gen.Return(row) # 注意task函數的返回值 class LoginHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): self.render('login.html') @gen.coroutine def post(self, *args, **kwargs): user = self.get_argument('user') data = yield gen.Task(get_user, user) # 執行Task函數,內部還是返回future對象。Task函數上第一個參數是要執行的函數,第二個是參數 if data: print(data) self.redirect('http://www.baidu.com') else: self.render('login.html') #原始方案,請求來了,連接數據庫,等待操作完成,關閉連接! # def post(self, *args, **kwargs): # user = self.get_argument('user') # # 連接數據庫: IO耗時 # # 查詢語句: IO耗時 # # 獲取結果 # data = {'id':1,'user':'alex'} # if data: # print(data) # self.redirect('http://www.baidu.com') # else: # self.render('login.html') application = tornado.web.Application([ (r"/login", LoginHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
