Tornado異步筆記(三)--- 持久連接 KeepAlive 簡(jiǎn)介

HTTP 持久連接

HTTP通信中爬迟,client和server一問(wèn)一答的方式。HTTP是基于TCP的應(yīng)用層協(xié)議,通常在發(fā)送請(qǐng)求之前需要?jiǎng)?chuàng)建TCP連接,然后在收到響應(yīng)之后會(huì)斷開(kāi)這個(gè)TCP連接隶垮。這就是常見(jiàn)的http短連接。既然有短連接秘噪,那么也有長(zhǎng)連接狸吞。

HTTP協(xié)議最初的設(shè)計(jì)是無(wú)連接無(wú)狀態(tài)的方式。為了維護(hù)狀態(tài),引入了cookie和session方式認(rèn)證識(shí)別用戶蹋偏。早期的web開(kāi)發(fā)中便斥,為了給用戶推送數(shù)據(jù),通常使用所謂的長(zhǎng)連接威始。那時(shí)的長(zhǎng)連接還是基于短連接的方式實(shí)現(xiàn)枢纠,即通過(guò)client的輪詢查詢,在用戶層面看起來(lái)連接并沒(méi)有斷開(kāi)黎棠。隨著技術(shù)的發(fā)展晋渺,又出現(xiàn)了Websockt和MQTT等通信協(xié)議。Websockt和MQTT則是全雙工的通信協(xié)議葫掉。

相比全雙工實(shí)現(xiàn)的長(zhǎng)連接些举,我們還會(huì)在web開(kāi)發(fā)中遇到長(zhǎng)連接跟狱。即HTTP協(xié)議中的keepalive模式俭厚。因?yàn)镠TTP設(shè)計(jì)是無(wú)連接設(shè)計(jì),請(qǐng)求應(yīng)答結(jié)束之后就關(guān)閉了TCP連接驶臊。在http通信中挪挤,就會(huì)有大量的新建和銷毀tcp連接的過(guò)程,那怕是同一個(gè)用戶同一個(gè)客戶端关翎。為了優(yōu)化這種方式扛门,HTTP提出了KeepAlive模式,即創(chuàng)建的tcp連接后纵寝,傳輸數(shù)據(jù)论寨,server返回響應(yīng)之后并不會(huì)關(guān)掉tcp連接,下一次http請(qǐng)求就能復(fù)用這個(gè)tcp連接爽茴。

這是一種協(xié)商式的連接葬凳,畢竟每次的http發(fā)送數(shù)據(jù)的時(shí)候,還是要單獨(dú)為每個(gè)請(qǐng)求發(fā)送header之類的信息室奏。相比全雙工的websocket火焰,一旦創(chuàng)建了連接,下一次就不需要再發(fā)送header胧沫,直接發(fā)送數(shù)據(jù)即可昌简。因此描述http的keepalive應(yīng)該是持久連接(HTTP persistent connection )更準(zhǔn)確。

keepalive 簡(jiǎn)介

HTTP的keepalive模式提供了HTTP通信的時(shí)候復(fù)用TCP連接的協(xié)商功能绒怨。http1.0默認(rèn)是關(guān)閉的纯赎,只有在http的header加入 Connection: Keep-Alive才能開(kāi)啟。而http1.1則正相反南蹂,默認(rèn)就打開(kāi)了犬金,只有顯示的在header里加入Connection: close才能關(guān)閉。現(xiàn)在的瀏覽器基本都是http1.1的協(xié)議,能否使用長(zhǎng)連接佑附,權(quán)看服務(wù)器的支持狀況了樊诺。下圖說(shuō)明了開(kāi)啟keepalive模式的持久連接與短連接的通信示意圖

短連接與持久連接,圖片來(lái)源網(wǎng)絡(luò)

當(dāng)開(kāi)啟了持久連接音同,就不能使用返回EOF的方式來(lái)判斷數(shù)據(jù)結(jié)尾了词爬。對(duì)于靜態(tài)和動(dòng)態(tài)的數(shù)據(jù),可以使用Conent-LenghtTransfer-Encoding`來(lái)做應(yīng)用層的區(qū)分权均。

requests與持久連接

了解了keeplive模式顿膨,接下來(lái)我們就來(lái)使用keepalive方式。服務(wù)器使用Tornado叽赊,tornado實(shí)現(xiàn)了keepalive的處理恋沃,客戶端我們可以分別使用同步的requests和異步的AsyncHTTPClient。

先寫(xiě)一個(gè)簡(jiǎn)單的服務(wù)器:

micro-server.py

import tornado.httpserver
import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.finish('It works')


app = tornado.web.Application(
        handlers=[
            ('/', IndexHandler),
        ],
        debug=True
)

if __name__ == '__main__':
    server = tornado.httpserver.HTTPServer(app)
    server.listen(8000)
    tornado.ioloop.IOLoop().instance().start()

requests 短連接

requests不愧是一個(gè)"for human" 的軟件必指,實(shí)現(xiàn)一個(gè)http客戶端非常簡(jiǎn)單囊咏。


import argparse

import requests

url = 'http://127.0.0.1:8000'


def short_connection():
    resp = requests.get(url)
    print(resp.text)

    resp = requests.get(url)
    print(resp.text)


def long_connection():
    pass


if __name__ == '__main__':
    ap = argparse.ArgumentParser()
    ap.add_argument("-t", "--type", default="short")
    args = ap.parse_args()
    type_ = args.type

    if type_ == 'short':
        short_connection()
    elif type_ == 'long':
        long_connection()

運(yùn)行 keepalive python requests-cli.py --type=short,可以看見(jiàn)返回了數(shù)據(jù)塔橡,同時(shí)通過(guò)另外一個(gè)神器wireshark抓包如下:

requests 短連接

從抓包的情況來(lái)看梅割,兩次http請(qǐng)求,一共創(chuàng)建了兩次tcp的握手連接和揮手?jǐn)嚅_(kāi)葛家。每次發(fā)送http數(shù)據(jù)都需要先創(chuàng)建tcp連接户辞,然后就斷開(kāi)了連接。通常是客戶端發(fā)起的斷開(kāi)連接癞谒。

requests 持久連接

requests的官網(wǎng)也說(shuō)明了底燎,基于urllib3的方式,requests百分比實(shí)現(xiàn)了keepalive方式弹砚,只需要?jiǎng)?chuàng)建一個(gè)客戶端session即可双仍,代碼如下:

def long_connection():
    s = requests.Session()

    resp = s.get(url)
    print(resp.text)

    resp = s.get(url)
    print(resp.text)

    s.close()

再次通過(guò)抓包如下圖:

requests 持久連接模式

可以看到,同樣也是兩次http請(qǐng)求迅栅,只創(chuàng)建了一次tcp的握手和揮手殊校。兩次http請(qǐng)求都基于一個(gè)tcp連接。再次查看包43读存,可以看到下圖中的報(bào)文header指定了keepalive为流。

http請(qǐng)求的數(shù)據(jù)包

AsyncHTTPClient與持久連接

tornado是一個(gè)優(yōu)秀高性能異步非阻塞(non-block)web框架。如果torando的handler中也需要請(qǐng)求別的三方資源让簿,使用requests的同步網(wǎng)絡(luò)IO敬察,將會(huì)block住整個(gè)tornado的進(jìn)程。因此tornado也實(shí)現(xiàn)了異步的http客戶端AsyncHTTPClient尔当。

短連接

使用AsyncHTTPClient也不難莲祸,但是想要使用其異步效果蹂安,就必須把其加入事件循環(huán)中,否則只有連接的創(chuàng)立锐帜,而沒(méi)有數(shù)據(jù)的傳輸就退出了田盈。

import tornado.httpclient
import tornado.ioloop
import time

url = 'http://127.0.0.1:8000'

def handle_response(response):
    if response.error:
        print("Error: %s" % response.error)
    else:
        print(response.body)

http_client = tornado.httpclient.AsyncHTTPClient()
http_client.fetch(url, handle_response)
http_client.fetch(url, handle_response)

運(yùn)行上述代碼,將會(huì)看到wirshark中缴阎,創(chuàng)建了兩次TCP連接和斷開(kāi)了連接允瞧,并沒(méi)有發(fā)送http數(shù)據(jù)。為了發(fā)送http數(shù)據(jù)蛮拔,還需要加入tornado的事件循環(huán)述暂。即在最后一行加入tornado.ioloop.IOLoop.instance().start()

再次運(yùn)行,客戶端正常收到了數(shù)據(jù)建炫,抓包如下:

async http client 短連接

抓包的結(jié)果咋一看像是持久連接畦韭,仔細(xì)一看卻有兩次握手和揮手的操作。的確肛跌,客戶端發(fā)送異步http請(qǐng)求的時(shí)候艺配,創(chuàng)建了兩個(gè)端口4998949990兩個(gè)tcp連接。因?yàn)槭钱惒降恼?qǐng)求惋砂,因此先創(chuàng)建了兩個(gè)連接妒挎,然后才發(fā)送數(shù)據(jù),發(fā)送數(shù)據(jù)的時(shí)候都是基于所創(chuàng)建的端口進(jìn)行的西饵。也就是沒(méi)有使用持久連接。

持久連接

AsyncHTTPClient使用持久連接也很簡(jiǎn)單×圮剑現(xiàn)在流行微服務(wù)架構(gòu)眷柔。通常提供給客戶端的服務(wù)稱之為網(wǎng)關(guān),網(wǎng)關(guān)從各種微服務(wù)中調(diào)用獲取數(shù)據(jù)原朝,通信的方式中驯嘱,同步的有http和rpc,異步的有mq之類的喳坠。而http通常都是使用持久連接的方式鞠评。

下面我們介紹一下在tornado server的handler中使用async client請(qǐng)求微服務(wù)的資源。

再寫(xiě)一個(gè)簡(jiǎn)單server

#!/usr/bin/env python
# -*- coding:utf-8 -*-


import tornado.gen
import tornado.httpclient
import tornado.httpserver
import tornado.ioloop
import tornado.web


class AsyncKeepAliveHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self, *args, **kwargs):
        
        url = 'http://127.0.0.1:8000/'

        http_client = tornado.httpclient.AsyncHTTPClient()
        response = yield tornado.gen.Task(http_client.fetch, url)
        print response.code
        print response.body
        self.finish("It works")

app = tornado.web.Application(
        handlers=[
            ('/async/keepalive', AsyncKeepAliveHandler)
        ],
        debug=True
)

if __name__ == '__main__':
    server = tornado.httpserver.HTTPServer(app)
    server.listen(5050)
    tornado.httpclient.AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
    tornado.ioloop.IOLoop().instance().start()

然后我們請(qǐng)求5050端口的服務(wù)壕鹉,也連接發(fā)送兩次http請(qǐng)求:

(venv)?  keepalive  curl http://127.0.0.1:5050/async/keepalive
It works%                                                                                                                                                     (venv)?  keepalive  curl http://127.0.0.1:5050/async/keepalive
It works%

再看我們的抓包情況:

tornado handler使用持久連接

從圖中可以看到剃幌,即使是兩個(gè)請(qǐng)求,最終都是復(fù)用了斷開(kāi)為50784的tcp連接晾浴。

因?yàn)閍synchttpclient默認(rèn)使用的是SimpleAsyncHTTPClient负乡,實(shí)現(xiàn)持久連接只需要配置一下tornado.httpclient.AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")即可。當(dāng)然脊凰,這個(gè)需要tornado的版本4.2以上抖棘,當(dāng)前的版本是4.5。

CurlAsyncHTTPClient依賴于pycurl。pycurl又依賴libcurl切省。在安裝pycurl的時(shí)候最岗,可能會(huì)出現(xiàn)link的問(wèn)題。例如ImportError: pycurl: libcurl link-time version (7.37.1) is older than compile-time version (7.43.0) 朝捆。 解決了link問(wèn)題仑性,如果是mac系統(tǒng),安裝的時(shí)候可能出現(xiàn)error: Setup script exited with error: command 'cc' failed右蹦,多半是由于xcode做鬼诊杆,這里有一個(gè)解決說(shuō)明

AsyncHTTPClient設(shè)置成為keepalive模式是全局性的,比較tornado是單進(jìn)程單線程的何陆,訪問(wèn)三方或者微服務(wù)晨汹,都是一個(gè)客戶端,所有的模式都是持久連接贷盲。

短連接與持久連接的應(yīng)用場(chǎng)景

持久連接可以減少tcp連接的創(chuàng)建和銷毀淘这,提升服務(wù)器的處理性能。但是并不是所有連接都得使用持久連接巩剖。長(zhǎng)短連接都有其使用場(chǎng)景铝穷。

既然持久連接在于連接的持久,因此對(duì)于頻繁通信佳魔,點(diǎn)對(duì)點(diǎn)的就可以使用曙聂。例如網(wǎng)關(guān)和微服務(wù)之間。如果創(chuàng)建了持久連接鞠鲜,就必須在意連接的存活狀態(tài)宁脊。客戶端一般不會(huì)主動(dòng)關(guān)閉贤姆,因此服務(wù)端需要維護(hù)這個(gè)連接狀態(tài)榆苞,對(duì)于一些長(zhǎng)時(shí)間沒(méi)有讀寫(xiě)事件發(fā)生的連接,可以主動(dòng)斷開(kāi)霞捡,節(jié)省資源坐漏。

對(duì)于一些用完就走的場(chǎng)景,也不需要使用持久連接碧信。而另外一些需要全雙工通信赊琳,例如推送和實(shí)時(shí)應(yīng)用,則需要真正的長(zhǎng)連接音婶,比如MQTT實(shí)現(xiàn)推送和websocket實(shí)現(xiàn)實(shí)時(shí)應(yīng)用等慨畸。

總結(jié)

微服務(wù)大行其道,從微觀來(lái)看衣式,增加了更多的網(wǎng)絡(luò)IO寸士。而IO又是最耗時(shí)的操作檐什。相比之下,程式的計(jì)算速度就顯得沒(méi)那么緊要了弱卡。優(yōu)化網(wǎng)絡(luò)IO才是提升性能的關(guān)鍵乃正。一些頻繁通信的場(chǎng)景,使用持久連接或長(zhǎng)連接更能優(yōu)化大量TCP連接的創(chuàng)建和銷毀婶博。

就Python的而言瓮具,Tornado的誕生就是為了解決網(wǎng)絡(luò)IO的瓶頸。并且很多tornado及其三方庫(kù)的問(wèn)題凡人,都能在github和stackoverflow找到作者的參與和回答名党。可見(jiàn)作者對(duì)項(xiàng)目的負(fù)責(zé)挠轴。由于tornado單線程的特性传睹,因此做任何IO操作,都需要考慮是否block岸晦。幸好有AsyncHTTPClinet欧啤,既可以提供異步IO,也可以實(shí)現(xiàn)持久連接启上,當(dāng)然邢隧,tornado也支持websocket。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末冈在,一起剝皮案震驚了整個(gè)濱河市倒慧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌讥邻,老刑警劉巖迫靖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異兴使,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)照激,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門发魄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人俩垃,你說(shuō)我怎么就攤上這事励幼。” “怎么了口柳?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵苹粟,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我跃闹,道長(zhǎng)嵌削,這世上最難降的妖魔是什么毛好? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮苛秕,結(jié)果婚禮上肌访,老公的妹妹穿的比我還像新娘。我一直安慰自己艇劫,他們只是感情好吼驶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著店煞,像睡著了一般蟹演。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上顷蟀,一...
    開(kāi)封第一講書(shū)人閱讀 51,274評(píng)論 1 300
  • 那天酒请,我揣著相機(jī)與錄音,去河邊找鬼衩椒。 笑死蚌父,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的毛萌。 我是一名探鬼主播苟弛,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼阁将!你這毒婦竟也來(lái)了膏秫?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤做盅,失蹤者是張志新(化名)和其女友劉穎缤削,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體吹榴,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亭敢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了图筹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帅刀。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖远剩,靈堂內(nèi)的尸體忽然破棺而出扣溺,到底是詐尸還是另有隱情,我是刑警寧澤瓜晤,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布锥余,位于F島的核電站,受9級(jí)特大地震影響痢掠,放射性物質(zhì)發(fā)生泄漏驱犹。R本人自食惡果不足惜嘲恍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望着绷。 院中可真熱鬧蛔钙,春花似錦、人聲如沸荠医。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)彬向。三九已至兼贡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間娃胆,已是汗流浹背遍希。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留里烦,地道東北人凿蒜。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像胁黑,于是被迫代替她去往敵國(guó)和親废封。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理丧蘸,服務(wù)發(fā)現(xiàn)漂洋,斷路器,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • 使用 HTTP 服務(wù)器或客戶端功能必須調(diào)用require('http')力喷。 Node 里的 HTTP 接口支持協(xié)議...
    保川閱讀 1,385評(píng)論 0 1
  • 第一章 Nginx簡(jiǎn)介 Nginx是什么 沒(méi)有聽(tīng)過(guò)Nginx刽漂?那么一定聽(tīng)過(guò)它的“同行”Apache吧!Ngi...
    JokerW閱讀 32,670評(píng)論 24 1,002
  • 本篇文章篇幅比較長(zhǎng)弟孟,先來(lái)個(gè)思維導(dǎo)圖預(yù)覽一下贝咙。 一、概述 1.計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)分層 2.TCP/IP 通信傳輸流 ...
    滌生_Woo閱讀 55,007評(píng)論 24 557
  • 今天朋友讓我?guī)兔Ω銈€(gè)Imei的格式拂募,我想這還不簡(jiǎn)單颈畸,一頓猛敲后,發(fā)現(xiàn)丫的光標(biāo)位置好坑没讲,現(xiàn)在我記錄下我的思路,如果有...
    Master_文閱讀 403評(píng)論 1 1