欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

構(gòu)建高效的python requests長(zhǎng)連接池詳解

 更新時(shí)間:2020年05月02日 08:56:40   作者:峰云就她了  
這篇文章主要介紹了構(gòu)建高效的python requests長(zhǎng)連接池詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧

前文:

最近在搞全網(wǎng)的CDN刷新系統(tǒng),在性能調(diào)優(yōu)時(shí)遇到了requests長(zhǎng)連接的一個(gè)問(wèn)題,以前關(guān)注過(guò)長(zhǎng)連接太多造成浪費(fèi)的問(wèn)題,但因?yàn)橄到y(tǒng)都是分布式擴(kuò)展的,針對(duì)這種各別問(wèn)題就懶得改動(dòng)了。 現(xiàn)在開(kāi)發(fā)的緩存刷新系統(tǒng),對(duì)于性能還是有些敏感的,我后面會(huì)給出最優(yōu)的http長(zhǎng)連接池構(gòu)建方式。

老生常談:

python下的httpclient庫(kù)哪個(gè)最好用? 我想大多數(shù)人還是會(huì)選擇requests庫(kù)的。原因么?也就是簡(jiǎn)單,易用!

如何蛋疼的構(gòu)建reqeusts的短連接請(qǐng)求:

python requests庫(kù)默認(rèn)就是長(zhǎng)連接的 (http 1.1, Connection: keep alive),如果單純?cè)趓equests頭部去掉Connection是不靠譜的,還需要借助httplib來(lái)配合.

s = requests.Session()

del s.headers['Connection']

正確發(fā)起 http 1.0的請(qǐng)求姿勢(shì)是:

#xiaorui.cc

import httplib
import requests

httplib.HTTPConnection._http_vsn = 10
httplib.HTTPConnection._http_vsn_str = 'HTTP/1.0'

r = requests.get('http://127.0.0.1:8888/')

服務(wù)端接收的http包體內(nèi)容:

GET / HTTP/1.0
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: python-requests/2.5.1 CPython/2.7.10 Darwin/15.4.0

所謂短連接就是發(fā)送 HTTP 1.0 協(xié)議,這樣web服務(wù)端當(dāng)然會(huì)在send完數(shù)據(jù)后,觸發(fā)close(),也就是傳遞 \0 字符串,達(dá)到關(guān)閉連接 ! 這里還是要吐槽一下,好多人天天說(shuō)系統(tǒng)優(yōu)化,連個(gè)基本的網(wǎng)絡(luò)io都不優(yōu)化,你還想干嘛。。。下面我們依次聊requests長(zhǎng)連接的各種問(wèn)題及性能優(yōu)化。

那么requests長(zhǎng)連接如何實(shí)現(xiàn)?

requests給我們提供了一個(gè)Session的長(zhǎng)連接類(lèi),他不僅僅能實(shí)現(xiàn)最基本的長(zhǎng)連接保持,還會(huì)附帶服務(wù)端返回的cookie數(shù)據(jù)。 在底層是如何實(shí)現(xiàn)的?

把HTTP 1.0 改成 HTTP 1.1 就可以了, 如果你標(biāo)明了是HTTP 1.1 ,那么有沒(méi)有 Connection: keep-alive 都無(wú)所謂的。 如果 HTTP 1.0加上Connection: keep-alive ,那么server會(huì)認(rèn)為你是長(zhǎng)連接。 就這么簡(jiǎn)單 !

poll([{fd=5, events=POLLIN}], 1, 0)  = 0 (Timeout)
sendto(5, "GET / HTTP/1.1\r\nHost: www.xiaorui.cc\r\nConnection: keep-alive\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nUser-Agent: python-requests/2.9.1\r\n\r\n", 144, 0, NULL, 0) = 144
fcntl(5, F_GETFL)      = 0x2 (flags O_RDWR)
fcntl(5, F_SETFL, O_RDWR)    = 0

Session的長(zhǎng)連接支持多個(gè)主機(jī)么? 也就是我在一個(gè)服務(wù)里先后訪問(wèn) a.com, b.com, c.com 那么requests session能否幫我保持連接 ?

答案很明顯,當(dāng)然是可以的!

但也僅僅是可以一用,但他的實(shí)現(xiàn)有很多的槽點(diǎn)。比如xiaorui.cc的主機(jī)上還有多個(gè)虛擬主機(jī),那么會(huì)出現(xiàn)什么情況么? 會(huì)不停的創(chuàng)建新連接,因?yàn)閞eqeusts的urllib3連接池管理是基于host的,這個(gè)host可能是域名,也可能ip地址,具體是什么,要看你的輸入。

strace -p 25449 -e trace=connect
Process 25449 attached - interrupt to quit
connect(13, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("61.216.13.196")}, 16) = 0
connect(8, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.202.72.116")}, 16) = 0
connect(8, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("125.211.204.141")}, 16) = 0
connect(8, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(8, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("153.37.238.190")}, 16) = 0
connect(8, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(8, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("157.255.128.103")}, 16) = 0
connect(8, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(8, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("139.215.203.190")}, 16) = 0
connect(8, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(8, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("42.56.76.104")}, 16) = 0
connect(8, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(8, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("42.236.125.104")}, 16) = 0
connect(8, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(8, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("110.53.246.11")}, 16) = 0
connect(8, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(8, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("36.248.26.191")}, 16) = 0
connect(8, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(8, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("125.211.204.151")}, 16) = 0

又比如你可能都是訪問(wèn)同一個(gè)域名,但是子域名不一樣,例子 a.xiaorui.cc, b.xiaorui.cc, c.xiaorui.cc, xxxx.xiaorui.cc,那么會(huì)造成什么問(wèn)題? 哪怕IP地址是一樣的,因?yàn)橛蛎灰粯?,那么requests session還是會(huì)幫你實(shí)例化長(zhǎng)連接。

python 24899 root 3u IPv4 27187722  0t0  TCP 101.200.80.162:59576->220.181.105.185:http (ESTABLISHED)
python 24899 root 4u IPv4 27187725  0t0  TCP 101.200.80.162:54622->101.200.80.162:http (ESTABLISHED)
python 24899 root 5u IPv4 27187741  0t0  TCP 101.200.80.162:59580->220.181.105.185:http (ESTABLISHED)
python 24899 root 6u IPv4 27187744  0t0  TCP 101.200.80.162:59581->220.181.105.185:http (ESTABLISHED)
python 24899 root 7u IPv4 27187858  0t0  TCP localhost:50964->localhost:http (ESTABLISHED)
python 24899 root 8u IPv4 27187880  0t0  TCP 101.200.80.162:54630->101.200.80.162:http (ESTABLISHED)
python 24899 root 9u IPv4 27187921  0t0  TCP 101.200.80.162:54632->101.200.80.162:http (ESTABLISHED)

如果是同一個(gè)二級(jí)域名,不同的url會(huì)發(fā)生呢? 是我們要的結(jié)果,只需要一個(gè)連接就可以了。

import requests
import time

s = requests.Session()
while 1:
 r = s.get('http://a.xiaorui.cc/1')
 r = s.get('http://a.xiaorui.cc/2')
 r = s.get('http://a.xiaorui.cc/3')

我們可以看到該進(jìn)程只實(shí)例化了一個(gè)長(zhǎng)連接。

# xiaorui.cc

python 27173 root 2u CHR 136,11  0t0  14 /dev/pts/11
python 27173 root 3u IPv4 27212480  0t0  TCP 101.200.80.162:36090->220.181.105.185:http (ESTABLISHED)
python 27173 root 12r CHR  1,9  0t0 3871 /dev/urandom

那么requests還有一個(gè)不是問(wèn)題的性能問(wèn)題。。。

requests session是可以保持長(zhǎng)連接的,但他能保持多少個(gè)長(zhǎng)連接? 10個(gè)長(zhǎng)連接! session內(nèi)置一個(gè)連接池,requests庫(kù)默認(rèn)值為10個(gè)長(zhǎng)連接。

requests.adapters.HTTPAdapter(pool_connections=100, pool_maxsize=100)

一般來(lái)說(shuō),單個(gè)session保持10個(gè)長(zhǎng)連接是絕對(duì)夠用了,但如果你是那種social爬蟲(chóng)呢?這么多域名只共用10個(gè)長(zhǎng)連接肯定不夠的。

python 28484 root 3u IPv4 27225486  0t0  TCP 101.200.80.162:54724->103.37.145.167:http (ESTABLISHED)
python 28484 root 4u IPv4 27225349  0t0  TCP 101.200.80.162:36583->120.132.34.62:https (ESTABLISHED)
python 28484 root 5u IPv4 27225490  0t0  TCP 101.200.80.162:46128->42.236.125.104:http (ESTABLISHED)
python 28484 root 6u IPv4 27225495  0t0  TCP 101.200.80.162:43162->222.240.172.228:http (ESTABLISHED)
python 28484 root 7u IPv4 27225613  0t0  TCP 101.200.80.162:37977->116.211.167.193:http (ESTABLISHED)
python 28484 root 8u IPv4 27225413  0t0  TCP 101.200.80.162:40688->106.75.67.54:http (ESTABLISHED)
python 28484 root 9u IPv4 27225417  0t0  TCP 101.200.80.162:59575->61.244.111.116:http (ESTABLISHED)
python 28484 root 10u IPv4 27225521  0t0  TCP 101.200.80.162:39199->218.246.0.222:http (ESTABLISHED)
python 28484 root 11u IPv4 27225524  0t0  TCP 101.200.80.162:46204->220.181.105.184:http (ESTABLISHED)
python 28484 root 12r CHR  1,9  0t0 3871 /dev/urandom
python 28484 root 14u IPv4 27225420  0t0  TCP 101.200.80.162:42684->60.28.124.21:http (ESTABLISHED)

讓我們看看requests的連接池是如何實(shí)現(xiàn)的? 通過(guò)代碼很容易得出Session()默認(rèn)的連接數(shù)及連接池是如何構(gòu)建的? 下面是requests的長(zhǎng)連接實(shí)現(xiàn)源碼片段。如需要再詳細(xì)的實(shí)現(xiàn)細(xì)節(jié),那就自己分析吧

# xiaorui.cc

class Session(SessionRedirectMixin):

 def __init__(self):
  ...
  self.max_redirects = DEFAULT_REDIRECT_LIMIT
  self.cookies = cookiejar_from_dict({})
  self.adapters = OrderedDict()
  self.mount('https://', HTTPAdapter()) # 如果沒(méi)有單獨(dú)配置adapter適配器,那么就臨時(shí)配置一個(gè)小適配器
  self.mount('http://', HTTPAdapter()) # 根據(jù)schema來(lái)分配不同的適配器adapter,上面是https,下面是http

  self.redirect_cache = RecentlyUsedContainer(REDIRECT_CACHE_SIZE)


class HTTPAdapter(BaseAdapter):

 def __init__(self, pool_connections=DEFAULT_POOLSIZE,
     pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES,
     pool_block=DEFAULT_POOLBLOCK):
  if max_retries == DEFAULT_RETRIES:
   self.max_retries = Retry(0, read=False)
  else:
   self.max_retries = Retry.from_int(max_retries)
  self.config = {}
  self.proxy_manager = {}

  super(HTTPAdapter, self).__init__()

  self._pool_connections = pool_connections
  self._pool_maxsize = pool_maxsize
  self._pool_block = pool_block

  self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block) # 連接池管理


DEFAULT_POOLBLOCK = False #是否阻塞連接池
DEFAULT_POOLSIZE = 10 # 默認(rèn)連接池
DEFAULT_RETRIES = 0 # 默認(rèn)重試次數(shù)
DEFAULT_POOL_TIMEOUT = None # 超時(shí)時(shí)間

Python requests連接池是借用urllib3.poolmanager來(lái)實(shí)現(xiàn)的。

每一個(gè)獨(dú)立的(scheme, host, port)元祖使用同一個(gè)Connection, (scheme, host, port)是從請(qǐng)求的URL中解析分拆出來(lái)的。

 from .packages.urllib3.poolmanager import PoolManager, proxy_from_url 。

下面是 urllib3的一些精簡(jiǎn)源碼, 可以看出他的連接池實(shí)現(xiàn)也是簡(jiǎn)單粗暴的。

# 解析url,分拆出scheme, host, port
def parse_url(url):
 """
 Example::
  >>> parse_url('http://google.com/mail/')
  Url(scheme='http', host='google.com', port=None, path='/mail/', ...)
  >>> parse_url('google.com:80')
  Url(scheme=None, host='google.com', port=80, path=None, ...)
  >>> parse_url('/foo?bar')
  Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...)

 return Url(scheme, auth, host, port, path, query, fragment)


# 獲取匹配的長(zhǎng)連接
def connection_from_url(self, url, pool_kwargs=None):
 u = parse_url(url)
 return self.connection_from_host(u.host, port=u.port, scheme=u.scheme, pool_kwargs=pool_kwargs)


# 獲取匹配host的長(zhǎng)連接
def connection_from_host(self, host, port=None, scheme='http', pool_kwargs=None):
 if scheme == "https":
  return super(ProxyManager, self).connection_from_host(
   host, port, scheme, pool_kwargs=pool_kwargs)

 return super(ProxyManager, self).connection_from_host(
  self.proxy.host, self.proxy.port, self.proxy.scheme, pool_kwargs=pool_kwargs)


# 根據(jù)url的三個(gè)指標(biāo)獲取連接
def connection_from_pool_key(self, pool_key, request_context=None):
 with self.pools.lock:
  pool = self.pools.get(pool_key)
  if pool:
   return pool

  scheme = request_context['scheme']
  host = request_context['host']
  port = request_context['port']
  pool = self._new_pool(scheme, host, port, request_context=request_context)
  self.pools[pool_key] = pool
 return pool


# 獲取長(zhǎng)連接的主入口
def urlopen(self, method, url, redirect=True, **kw):
 u = parse_url(url)
 conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme)

這里為止,Python requests關(guān)于session連接類(lèi)實(shí)現(xiàn),說(shuō)的算明白了。 但就requests和urllib3的連接池實(shí)現(xiàn)來(lái)說(shuō),還是有一些提升空間的。 但問(wèn)題來(lái)了,單單靠著域名和端口會(huì)造成一些問(wèn)題,至于造成什么樣子的問(wèn)題,我在上面已經(jīng)有詳細(xì)的描述了。

那么如何解決?

我們可以用 scheme + 主domain + host_ip + port 來(lái)實(shí)現(xiàn)長(zhǎng)連接池的管理。

其實(shí)大多數(shù)的場(chǎng)景是無(wú)需這么細(xì)致的實(shí)現(xiàn)連接池的,但根據(jù)我們的測(cè)試的結(jié)果來(lái)看,在服務(wù)初期性能提升還是不小的。

這樣既解決了域名ip輪詢(xún)帶來(lái)的連接重置問(wèn)題,也解決了多級(jí)域名下不能共用連接的問(wèn)題。

以上這篇構(gòu)建高效的python requests長(zhǎng)連接池詳解就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 18?個(gè)?Python?編程技巧,提高工作效率

    18?個(gè)?Python?編程技巧,提高工作效率

    這篇文章主要分享的是18個(gè)Python編程技巧,文章圍繞?Python的相關(guān)資料展開(kāi)詳細(xì)全文,對(duì)工作中的你了能具有一定的參考價(jià)值,需要的朋友可以參考一下,希望對(duì)你有所幫助
    2022-01-01
  • Python代碼模擬CPU工作原理

    Python代碼模擬CPU工作原理

    Python代碼來(lái)實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的CPU。用代碼模擬大的部件,使大家從原理上理解CPU工作。使它可編程,支持加減法運(yùn)算、讀寫(xiě)內(nèi)存、無(wú)條件跳轉(zhuǎn)、條件跳轉(zhuǎn)的功能。
    2023-01-01
  • Python可變參數(shù)*args和**kwargs用法實(shí)例小結(jié)

    Python可變參數(shù)*args和**kwargs用法實(shí)例小結(jié)

    這篇文章主要介紹了Python可變參數(shù)*args和**kwargs用法,結(jié)合實(shí)例形式總結(jié)分析了Python中可變參數(shù)*args和**kwargs的功能、區(qū)別與具體使用技巧,需要的朋友可以參考下
    2018-04-04
  • Python中.py程序在CMD控制臺(tái)以指定虛擬環(huán)境運(yùn)行

    Python中.py程序在CMD控制臺(tái)以指定虛擬環(huán)境運(yùn)行

    本文主要介紹了Python中.py程序在CMD控制臺(tái)以指定虛擬環(huán)境運(yùn)行,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • 解決c++調(diào)用python中文亂碼問(wèn)題

    解決c++調(diào)用python中文亂碼問(wèn)題

    這篇文章主要介紹了c++調(diào)用python中文亂碼問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Tkinter組件Entry的具體使用

    Tkinter組件Entry的具體使用

    本文主要介紹了Tkinter組件Entry的具體使用,Entry組件通常用于獲取用戶(hù)的輸入文本,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • 在python中利用dict轉(zhuǎn)json按輸入順序輸出內(nèi)容方式

    在python中利用dict轉(zhuǎn)json按輸入順序輸出內(nèi)容方式

    今天小編就為大家分享一篇在python中利用dict轉(zhuǎn)json按輸入順序輸出內(nèi)容方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-02-02
  • Python 通過(guò)微信控制實(shí)現(xiàn)app定位發(fā)送到個(gè)人服務(wù)器再轉(zhuǎn)發(fā)微信服務(wù)器接收位置信息

    Python 通過(guò)微信控制實(shí)現(xiàn)app定位發(fā)送到個(gè)人服務(wù)器再轉(zhuǎn)發(fā)微信服務(wù)器接收位置信息

    這篇文章主要介紹了Python 通過(guò)微信控制實(shí)現(xiàn)app定位發(fā)送到個(gè)人服務(wù)器,再轉(zhuǎn)發(fā)微信服務(wù)器接收位置信息,本文給出了實(shí)例代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò)具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-08-08
  • python買(mǎi)賣(mài)股票的最佳時(shí)機(jī)(基于貪心/蠻力算法)

    python買(mǎi)賣(mài)股票的最佳時(shí)機(jī)(基于貪心/蠻力算法)

    這篇文章主要介紹了python買(mǎi)賣(mài)股票的最佳時(shí)機(jī)(基于貪心/蠻力算法),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • 淺談python中的數(shù)字類(lèi)型與處理工具

    淺談python中的數(shù)字類(lèi)型與處理工具

    下面小編就為大家?guī)?lái)一篇淺談python中的數(shù)字類(lèi)型與處理工具。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08

最新評(píng)論