阻塞式:(適用於全部框架,Django,Flask,Tornado,Bottle)
一個請求到來未處理完成,後續一直等待
解決方案:多線程,多進程
異步非阻塞(存在IO請求): Tornado(單進程+單線程)
使用異步非阻塞,須要遵循Tornado框架內部規則,gen
多個鏈接請求,鏈接給服務端,若是是有異步非阻塞的話,服務端會接收全部的請求交由後臺處理,等待其餘連接的同時,原先鏈接不斷開,直至返回後臺處理完成的結果!
外部請求,鏈接服務端 或在select中建立Future對象,而後服務端再把請求交給業務處理平臺,此時select監聽的列表中又會生成一個socket對象,當業務平臺對請求處理完成以後就會把信息返回到服務端的select監聽列表中,同時對這個Future對象賦值,用於標記服務端是否要給客戶端返回請求信息。html
執行流程,本質上都是返回一個future對象,若是對這個對象被set_result了就返回值,不然就是夯住,一直保持鏈接,不終止請求。python
一、基本使用mysql
裝飾器 + Future 從而實現Tornado的異步非阻塞git
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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方法。github
異步非阻塞體如今當在Tornaod等待用戶向future對象中放置數據時,還能夠處理其餘請求。web
注意:在等待用戶向future對象中放置數據或信號時,此鏈接是不斷開的。sql
二、同步阻塞和異步非阻塞對比數據庫
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()
三、httpclient類庫多線程
Tornado提供了httpclient類庫用於發送Http請求,其配合Tornado的異步非阻塞使用。app
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
#!/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()