Aiohttp 是 Python 的一個 HTTP 框架,基於 asyncio,因此叫 Aiohttp。html
我主要是看源碼,想理解它的設計,因此附上了類圖與時序圖。不可避免的,可讀性會比較差。
想找教程的話,請移步 官方教程,寫得仍是挺不錯的。python
下面這個例子,經過 HTTP GET 列出 GitHub 的 public events:git
import asyncio import aiohttp async def main(): async with aiohttp.ClientSession() as session: async with session.get('https://api.github.com/events') as resp: print(resp.status) print(await resp.text()) loop = asyncio.get_event_loop() loop.run_until_complete(main())
Response 是一個 JSON 格式的文本:github
[ { "id": "6888907432", "type": "PushEvent", "actor": { "id": 3956266, "login": "sekineh", "display_login": "sekineh", "gravatar_id": "", "url": "https://api.github.com/users/sekineh", "avatar_url": "https://avatars.githubusercontent.com/u/3956266?" }, ... ]
ClientSession
是一個 Asynchronous Context Manager,因此搭配 async with
語句一塊兒使用。像下面這樣應該也是能夠的:json
async def main(): session = aiohttp.ClientSession() ... await session.close()
不過確定是不推薦的,就當是幫助理解吧。api
ClientSession.get()
返回一個 ClientResponse
對象,經過 text()
方法,能夠拿到 response 的文本:session
print(await resp.text())
固然,text()
是一個協程:app
@asyncio.coroutine def text(self, encoding=None, errors='strict'): """Read response payload and decode.""" ...
ClientSession
依賴 Connector 來建立鏈接,缺省爲 TCPConnector
,它繼承自 BaseConnector
,此外還有 UnixConnector
(應該是 Unix Domain Socket)。框架
Connector 的接口比較簡單,主要提供了 connect()
方法(也是協程):異步
@asyncio.coroutine def connect(self, req): """Get from pool or create new connection.""" ...
以及 close()
方法:
def close(self): """Close all opened transports.""" ...
ClientRequest
有個屬性 connection_key
:
class ClientRequest: @property def connection_key(self): return ConnectionKey(self.host, self.port, self.ssl)
它是一個 namedtuple
:
ConnectionKey = namedtuple('ConnectionKey', ['host', 'port', 'ssl'])
由 host
,port
和 ssl
三個元素組成,這三個元素能夠惟必定義一個鏈接,因此叫 ConnectionKey
。
文章開頭的那個例子中,ConnectionKey 爲:
ConnectionKey(host='api.github.com', port=443, ssl=True)
Aiohttp 爲 Client 程序提供了一個全局函數 request()
,用法以下:
async def main(): resp = await aiohttp.request('GET', 'http://python.org/') print(resp) loop = asyncio.get_event_loop() loop.run_until_complete(main())
可見 request()
只是 ClientSession
的一個簡單封裝,其步驟大體爲:
TCPConnector
;ClientSession
;ClientSession._request()
。建議不要直接使用 request()
,而只把它當成 ClientSession
的一個樣例。由於 Aiohttp 官方文檔是 這樣說的:
Don’t create a session per request. Most likely you need a session per application which performs all requests altogether.A session contains a connection pool inside, connection reusage and keep-alives (both are on by default) may speed up total performance.
即,一個 request 用一個 session,太浪費;一般是一個 application 用一個 session。
我常常發現一個變量,明明能夠是局部變量,卻被當成了成員變量。
Request 裏放了一個 response?
class ClientRequest: def send(self, conn): ... self.response = self.response_class( self.method, self.original_url, writer=self._writer, ... ) self.response._post_init(self.loop, self._session) return self.response
self.response
在 ClientRequest
其餘地方並無用到,是否能夠改爲局部變量?
ClientResponse.start()
裏的 _protocol
應該用局部變量吧?
class ClientResponse: @asyncio.coroutine def start(self, connection, read_until_eof=False): """Start response processing.""" self._closed = False self._protocol = connection.protocol
The End