用到知識點:logging、asyncio、aiohttp模塊(後續補充知識點)html
一、logging(日誌)模塊nginx
功能:web
提供了標準的日誌接口,記錄程序日誌,包含正常的程序訪問日誌、錯誤、警告等信息輸出編程
能夠存爲各類格式的日誌數據瀏覽器
日誌分爲:debug()、info()、warning()、error()、critical()5個級別(級別:DEBUG < INFO < WARNING < ERROR < CRITICAL)服務器
import logging print(logging.DEBUG) # 10 print(logging.INFO) # 20 print(logging.WARNING) # 30 print(logging.ERROR) # 40 print(logging.CRITICAL) # 50
默認輸出只顯示大於等於WARNING的日誌網絡
import logging logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message') """ 輸出: WARNING:root:warning message ERROR:root:error message CRITICAL:root:critical message """
經過logging.basicConfig()函數靈活配置日誌級別,日誌格式,輸出位置併發
在logging.basicConfig()函數中可經過具體參數來更改logging模塊默認行爲,可用參數有:app
filename:用指定的文件名建立FiledHandler(後邊會具體講解handler的概念),這樣日誌會被存儲在指定的文件中。框架
filemode:文件打開方式,在指定了filename時使用這個參數,默認值爲「a」還可指定爲「w」。
format:指定handler使用的日誌顯示格式。
datefmt:指定日期時間格式。
level:設置rootlogger(後邊會講解具體概念)的日誌級別
stream:用指定的stream建立StreamHandler。能夠指定輸出到sys.stderr,sys.stdout或者文件,默認爲sys.stderr。若同時列出了filename和stream兩個參數,則stream參數會被忽略。
format參數中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 數字形式的日誌級別
%(levelname)s 文本形式的日誌級別
%(pathname)s 調用日誌輸出函數的模塊的完整路徑名,可能沒有
%(filename)s 調用日誌輸出函數的模塊的文件名
%(module)s 調用日誌輸出函數的模塊名
%(funcName)s 調用日誌輸出函數的函數名
%(lineno)d 調用日誌輸出函數的語句所在的代碼行
%(created)f 當前時間,用UNIX標準的表示時間的浮 點數表示
%(relativeCreated)d 輸出日誌信息時的,自Logger建立以 來的毫秒數
%(asctime)s 字符串形式的當前時間。默認格式是 「2003-07-08 16:49:45,896」。逗號後面的是毫秒
%(thread)d 線程ID。可能沒有
%(threadName)s 線程名。可能沒有
%(process)d 進程ID。可能沒有
%(message)s用戶輸出的消息
import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S', filename='test.log', filemode='w') """ level=logging.DEBUG:修改默認輸出級別爲Debug format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s':指定handler使用的日誌顯示格式。 %(asctime)s:字符串形式的當前時間 %(filename)s:調用日誌輸出函數的模塊的文件名 [line:%(lineno)d]:調用日誌輸出函數的語句所在的代碼行 %(levelname)s:文本形式的日誌級別 %(message)s:用戶輸出的消息 datefmt='%a, %d %b %Y %H:%M:%S':設置日期格式 %a:星期 %d:日期 %b:月份 %Y:年 %H:%M:%S:時:分:秒 filename='test.log':設置日誌輸出文件 filemode='w':設置日誌輸出文件打開方式 """ logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message') """ test.log文件內容: Wed, 21 Jun 2017 16:54:30 test.py[line:12] DEBUG debug message Wed, 21 Jun 2017 16:54:30 test.py[line:13] INFO info message Wed, 21 Jun 2017 16:54:30 test.py[line:14] WARNING warning message Wed, 21 Jun 2017 16:54:30 test.py[line:15] ERROR error message Wed, 21 Jun 2017 16:54:30 test.py[line:16] CRITICAL critical message """
二、asyncio(異步IO)模塊
用協程實現生產消息者模型
其實next()和send()在必定意義上做用是類似的,區別是send()能夠傳遞yield表達式的值進去,而next()只能傳遞None進去。所以c.next() 和 c.send(None) 做用是同樣的。
第一次調用時,請使用next()語句或是send(None),不能使用send發送一個非None的值,不然會出錯的,由於沒有Python yield語句來接收這個值。
def consumer(): r = '' while True: n = yield r # 跳出生成器,n沒有定義 if not n: return print('[CONSUMER] Consuming %s...' %n) r = '200 OK' def produce(c): c.send(None) # 啓動生成器,從生成器函數的第一行代碼開始執行 n = 0 while n < 5: n = n + 1 print('[PRODUCE] Producing %s....' %n) r = c.send(n) #獲取yield返回值 print('[PRODUCE] Consumer return %s....' %r) c.close() # 關閉consumer,整個過程結束 c = consumer() # 建立生成器對象 produce(c) """ 輸出: [PRODUCE] Producing 1.... [CONSUMER] Consuming 1... [PRODUCE] Consumer return 200 OK.... [PRODUCE] Producing 2.... [CONSUMER] Consuming 2... [PRODUCE] Consumer return 200 OK.... [PRODUCE] Producing 3.... [CONSUMER] Consuming 3... [PRODUCE] Consumer return 200 OK.... [PRODUCE] Producing 4.... [CONSUMER] Consuming 4... [PRODUCE] Consumer return 200 OK.... [PRODUCE] Producing 5.... [CONSUMER] Consuming 5... [PRODUCE] Consumer return 200 OK.... """
asyncio
的編程模型就是一個消息循環,經過async關鍵字定義一個協程(coroutine),協程也是一種對象。
協程不能直接運行,須要把協程加入到事件循環(loop),由後者在適當的時候調用協程。
asyncio.get_event_loop
方法能夠建立一個事件循環,而後使用run_until_complete
將協程註冊到事件循環,並啓動事件循環。
import asyncio async def hello(): # async定義一個協程 print('hello world!') # await用於掛起阻塞的異步調用接口。 # 異步調用,把asyncio.sleep(1)當作是一個耗時1秒的IO操做 r = await asyncio.sleep(1) print('hello again!') # 建立一個事件循環 loop = asyncio.get_event_loop() # 將協程註冊到事件循環,並啓動事件循環 loop.run_until_complete(hello()) # 關閉事件循環 loop.close()
用task封裝兩個coroutine
import asyncio import threading async def hello(): # async定義一個協程 print('hello world! (%s)' %threading.current_thread()) # await用於掛起阻塞的異步調用接口。 # 異步調用,把asyncio.sleep(1)當作是一個耗時1秒的IO操做 r = await asyncio.sleep(1) print('hello again! (%s)' %threading.current_thread()) # 建立一個事件循環 loop = asyncio.get_event_loop() tasks = [hello(), hello()] # 將協程註冊到事件循環,並啓動事件循環 loop.run_until_complete(asyncio.wait(tasks)) # 關閉事件循環 loop.close() """ 輸出: hello world! (<_MainThread(MainThread, started 13160)>) hello world! (<_MainThread(MainThread, started 13160)>) (間隔約1秒後再輸出下面) hello again! (<_MainThread(MainThread, started 13160)>) hello again! (<_MainThread(MainThread, started 13160)>) """
咱們用asyncio
的異步網絡鏈接來獲取sina、sohu和163的網站首頁
import asyncio async def wget(host): # async定義一個協程 print('wget %s...' %host) connect = asyncio.open_connection(host, 80) reader, writer = await connect header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host writer.write(header.encode('utf-8')) # 刷新底層傳輸的寫緩衝區。也就是把須要發送出去的數據,從緩衝區發送出去 await writer.drain() while True: line = await reader.readline() if line == b'\r\n': break print('%s header > %s' % (host, line.decode('utf-8').rstrip())) writer.close() # 建立一個事件循環 loop = asyncio.get_event_loop() tasks = [wget(host) for host in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']] # 將協程註冊到事件循環,並啓動事件循環 loop.run_until_complete(asyncio.wait(tasks)) # 關閉事件循環 loop.close() """ 輸出: wget www.sohu.com... wget www.sina.com.cn... wget www.163.com... www.163.com header > HTTP/1.0 302 Moved Temporarily www.163.com header > Server: Cdn Cache Server V2.0 www.163.com header > Date: Wed, 21 Jun 2017 11:13:32 GMT www.163.com header > Content-Length: 0 www.163.com header > Location: http://www.163.com/special/0077jt/error_isp.html www.163.com header > Connection: close www.sina.com.cn header > HTTP/1.1 200 OK www.sina.com.cn header > Server: nginx www.sina.com.cn header > Date: Wed, 21 Jun 2017 11:12:45 GMT www.sina.com.cn header > Content-Type: text/html www.sina.com.cn header > Last-Modified: Wed, 21 Jun 2017 11:11:09 GMT www.sina.com.cn header > Vary: Accept-Encoding www.sina.com.cn header > Expires: Wed, 21 Jun 2017 11:13:45 GMT www.sina.com.cn header > Cache-Control: max-age=60 www.sina.com.cn header > X-Powered-By: shci_v1.03 www.sina.com.cn header > Age: 47 www.sina.com.cn header > Content-Length: 597663 www.sina.com.cn header > X-Cache: HIT from cnc.tianjin.sinacache.91.nb.sinaedge.com www.sina.com.cn header > Connection: close www.sohu.com header > HTTP/1.1 200 OK www.sohu.com header > Content-Type: text/html;charset=UTF-8 www.sohu.com header > Connection: close www.sohu.com header > Server: nginx www.sohu.com header > Date: Wed, 21 Jun 2017 11:13:27 GMT www.sohu.com header > Cache-Control: max-age=60 www.sohu.com header > X-From-Sohu: X-SRC-Cached www.sohu.com header > Content-Encoding: gzip www.sohu.com header > FSS-Cache: HIT from 8212655.14766265.9212435 www.sohu.com header > FSS-Proxy: Powered by 2969695.4280425.3969395 """
三、asyncio
能夠實現單線程併發IO操做。
asyncio
用在服務器端,例如Web服務器,因爲HTTP鏈接就是IO操做,所以能夠用單線程+coroutine
實現多用戶的高併發支持。
asyncio
實現了TCP、UDP、SSL等協議,aiohttp
則是基於asyncio
實現的HTTP框架。
編寫一個HTTP服務器,分別處理如下URL:
/
- 首頁返回b'<h1>Index</h1>'
;
/hello/{name}
- 根據URL參數返回文本hello, %s!
import asyncio from aiohttp import web async def index(request): await asyncio.sleep(0.5) return web.Response(body=b'<h1>Index</h1>', content_type='text/html') async def hello(request): await asyncio.sleep(0.5) text = '<h1>hello, %s!</h1>' %request.match_info['name'] return web.Response(body=text.encode('utf-8'), content_type='text/html') async def init(loop): app = web.Application(loop=loop) app.router.add_route('GET', '/', index) app.router.add_route('GET', '/hello/{name}', hello) srv = await loop.create_server(app.make_handler(), '127.0.0.1', 8500) print('Server started at http://127.0.0.1:8500...') return srv loop = asyncio.get_event_loop() loop.run_until_complete(init(loop)) loop.run_forever()
一、輸入瀏覽器:
http://127.0.0.1:8500/hello/char
瀏覽器輸出:
二、輸入瀏覽器:
http://127.0.0.1:8500
瀏覽器輸出:
""" app.py編寫Web App骨架 """ import logging; logging.basicConfig(level=logging.INFO) import asyncio from aiohttp import web async def index(request): """響應函數""" return web.Response(body=b'<h1>Awesome</h1>', content_type='text/html') async def init(loop): """Web App服務器初始化""" # 製做響應合集 app = web.Application(loop=loop) # 把響應函數添加到響應函數集合 app.router.add_route(method='GET', path='/', handler=index) # 建立服務器(鏈接網址、端口,綁定handler) srv = await loop.create_server(app.make_handler(), '127.0.0.1', 9000) logging.info('server start at http://127.0.0.1:9000') return srv # 建立事件 loop = asyncio.get_event_loop() # 運行 loop.run_until_complete(init(loop)) # 服務器不關閉 loop.run_forever()