python後臺——asyncio,aiohttp教程

很多朋友對異步編程都處於“聽說很強大”的認知狀態。鮮有在生產項目中使用它。而使用它的同學,則大多數都停留在知道如何使用 Tornado、Twisted、Gevent 這類異步框架上,出現各種古怪的問題難以解決。而且使用了異步框架的部分同學,由於用法不對,感覺它並沒牛逼到哪裏去,所以很多同學做 Web 後端服務時還是採用 Flask、Django等傳統的非異步框架。

從上兩屆 PyCon 技術大會看來,異步編程已經成了 Python 生態下一階段的主旋律。如新興的 Go、Rust、Elixir 等編程語言都將其支持異步和高併發作爲主要“賣點”,技術變化趨勢如此。Python 生態爲不落人後,從2013年起由 Python 之父 Guido 親自操刀主持了Tulip(asyncio)項目的開發。

異步io的好處在於避免的線程的開銷和切換,而且我們都知道python其實是沒有多線程的,只是通過底層線層鎖實現的多線程。另一個好處在於避免io操作(包含網絡傳輸)的堵塞時間。

asyncio可以實現單線程併發IO操作。如果僅用在客戶端,發揮的威力不大。如果把asyncio用在服務器端,例如Web服務器,由於HTTP連接就是IO操作,因此可以用單線程+coroutine實現多用戶的高併發支持。

asyncio實現了TCP、UDP、SSL等協議,aiohttp則是基於asyncio實現的HTTP框架。

對於異步io你需要知道的重點,要注意的是,await語法只能出現在通過async修飾的函數中,否則會報SyntaxError錯誤。而且await後面的對象需要是一個Awaitable,或者實現了相關的協議。

注意:

所有需要異步執行的函數,都需要asyncio中的輪訓器去輪訓執行,如果函數阻塞,輪訓器就會去執行下一個函數。所以所有需要異步執行的函數都需要加入到這個輪訓器中。

例如:

import requests
import time
import asyncio

# 創建一個異步函數
async def task_func():
    await asyncio.sleep(1)
    resp = requests.get('http://192.168.2.177:5002/')
    print('2222222',time.time(),resp.text)


async def main(loop):
    loop=asyncio.get_event_loop()   # 獲取全局輪訓器
    task = loop.create_task(task_func())  # 在全局輪訓器加入協成,只有加入全局輪訓器才能被監督執行
    await asyncio.sleep(2)   # 等待兩秒爲了不要立即執行event_loop.close(),項目中event_loop應該是永不停歇的
    print('11111111111',time.time())


event_loop = asyncio.get_event_loop()
try:
    event_loop.run_until_complete(main(event_loop))
finally:
    event_loop.close()    # 當輪訓器關閉以後,所有沒有執行完成的協成將全部關閉

下面是aiohttp作爲服務器端的一個簡單的demo。

#!/usr/bin/env python3
import argparse
from aiohttp import web
import asyncio
import base64
import logging
import uvloop
import time,datetime
import json
import requests

asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())


routes = web.RouteTableDef()


@routes.get('/')
async def hello(request):
    return web.Response(text="Hello, world")


# 定義一個路由映射,接收網址參數,post方式
@routes.post('/demo1/{name}')
async def demo1(request):   # 異步監聽,只要一有握手就開始觸發,此時網址參數中的name就已經知道了,但是前端可能還沒有完全post完數據。
    name = request.match_info.get('name', "Anonymous")  # 獲取name
    print(datetime.datetime.now())   # 觸發視圖函數的時間
    data = await request.post()    # 等待post數據完成接收,只有接收完成才能進行後續操作.data['key']獲取參數
    print(datetime.datetime.now())   # 接收post數據完成的時間
    logging.info('safety dect request start %s' % datetime.datetime.now())
    result = {'name':name,'key':data['key']}
    logging.info('safety dect request finish %s, %s' % (datetime.datetime.now(),json.dumps(result)))
    return web.json_response(result)


# 定義一個路由映射,設計到io操作
@routes.post('/demo2')
async def demo2(request):   # 異步監聽,只要一有握手就開始觸發,此時網址參數中的name就已經知道了,但是前端可能還沒有完全post完數據。
    data = await request.post()    # 等待post數據完成接收,只有接收完成才能進行後續操作.data['key']獲取參數
    logging.info('safety dect request start %s' % datetime.datetime.now())
    res = requests.post('http://www.baidu.com')   # 網路id,會自動切換到其他協成上
    logging.info('safety dect request finish %s' % res.test)
    return web.Response(text="welcome")




if __name__ == '__main__':

    logging.info('server start')
    app = web.Application()
    app.add_routes(routes)
    web.run_app(app,host='0.0.0.0',port=8080)
    logging.info('server close')

aiohttp的另一個主要作用是作爲異步客戶端,用來解決高併發請求的情況。比如現在我要模擬一個高併發請求來測試我的服務器負載情況。所以需要在python裏模擬高併發。高併發可以有多種方式,比如多線程,但是由於python本質上是沒有多線程的,通過底層線程鎖實現的多線程。在模型高併發時,具有線程切換和線程開銷的損耗。所以我們就可以使用多協成來實現高併發。

我們就可以使用aiohttp來模擬高併發客戶端。demo如下,用來模擬多個客戶端向指定服務器post圖片。

這裏寫代碼片
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章