电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

構建高效的python requests長連接池詳解

瀏覽:18日期:2022-07-26 17:04:48

前文:

最近在搞全網的CDN刷新系統,在性能調優時遇到了requests長連接的一個問題,以前關注過長連接太多造成浪費的問題,但因為系統都是分布式擴展的,針對這種各別問題就懶得改動了。 現在開發的緩存刷新系統,對于性能還是有些敏感的,我后面會給出最優的http長連接池構建方式。

老生常談:

python下的httpclient庫哪個最好用? 我想大多數人還是會選擇requests庫的。原因么?也就是簡單,易用!

如何蛋疼的構建reqeusts的短連接請求:

python requests庫默認就是長連接的 (http 1.1, Connection: keep alive),如果單純在requests頭部去掉Connection是不靠譜的,還需要借助httplib來配合.

s = requests.Session()

del s.headers[’Connection’]

正確發起 http 1.0的請求姿勢是:

#xiaorui.ccimport httplibimport requestshttplib.HTTPConnection._http_vsn = 10httplib.HTTPConnection._http_vsn_str = ’HTTP/1.0’r = requests.get(’http://127.0.0.1:8888/’)

服務端接收的http包體內容:

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

所謂短連接就是發送 HTTP 1.0 協議,這樣web服務端當然會在send完數據后,觸發close(),也就是傳遞 0 字符串,達到關閉連接 ! 這里還是要吐槽一下,好多人天天說系統優化,連個基本的網絡io都不優化,你還想干嘛。。。下面我們依次聊requests長連接的各種問題及性能優化。

那么requests長連接如何實現?

requests給我們提供了一個Session的長連接類,他不僅僅能實現最基本的長連接保持,還會附帶服務端返回的cookie數據。 在底層是如何實現的?

把HTTP 1.0 改成 HTTP 1.1 就可以了, 如果你標明了是HTTP 1.1 ,那么有沒有 Connection: keep-alive 都無所謂的。 如果 HTTP 1.0加上Connection: keep-alive ,那么server會認為你是長連接。 就這么簡單 !

poll([{fd=5, events=POLLIN}], 1, 0) = 0 (Timeout)sendto(5, 'GET / HTTP/1.1rnHost: www.xiaorui.ccrnConnection: keep-alivernAccept-Encoding: gzip, deflaternAccept: */*rnUser-Agent: python-requests/2.9.1rnrn', 144, 0, NULL, 0) = 144fcntl(5, F_GETFL) = 0x2 (flags O_RDWR)fcntl(5, F_SETFL, O_RDWR) = 0

Session的長連接支持多個主機么? 也就是我在一個服務里先后訪問 a.com, b.com, c.com 那么requests session能否幫我保持連接 ?

答案很明顯,當然是可以的!

但也僅僅是可以一用,但他的實現有很多的槽點。比如xiaorui.cc的主機上還有多個虛擬主機,那么會出現什么情況么? 會不停的創建新連接,因為reqeusts的urllib3連接池管理是基于host的,這個host可能是域名,也可能ip地址,具體是什么,要看你的輸入。

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

又比如你可能都是訪問同一個域名,但是子域名不一樣,例子 a.xiaorui.cc, b.xiaorui.cc, c.xiaorui.cc, xxxx.xiaorui.cc,那么會造成什么問題? 哪怕IP地址是一樣的,因為域名不一樣,那么requests session還是會幫你實例化長連接。

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)

如果是同一個二級域名,不同的url會發生呢? 是我們要的結果,只需要一個連接就可以了。

import requestsimport times = 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’)

我們可以看到該進程只實例化了一個長連接。

# xiaorui.ccpython 27173 root 2u CHR 136,11 0t0 14 /dev/pts/11python 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還有一個不是問題的性能問題。。。

requests session是可以保持長連接的,但他能保持多少個長連接? 10個長連接! session內置一個連接池,requests庫默認值為10個長連接。

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

一般來說,單個session保持10個長連接是絕對夠用了,但如果你是那種social爬蟲呢?這么多域名只共用10個長連接肯定不夠的。

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/urandompython 28484 root 14u IPv4 27225420 0t0 TCP 101.200.80.162:42684->60.28.124.21:http (ESTABLISHED)

讓我們看看requests的連接池是如何實現的? 通過代碼很容易得出Session()默認的連接數及連接池是如何構建的? 下面是requests的長連接實現源碼片段。如需要再詳細的實現細節,那就自己分析吧

# xiaorui.ccclass Session(SessionRedirectMixin): def __init__(self): ... self.max_redirects = DEFAULT_REDIRECT_LIMIT self.cookies = cookiejar_from_dict({}) self.adapters = OrderedDict() self.mount(’https://’, HTTPAdapter()) # 如果沒有單獨配置adapter適配器,那么就臨時配置一個小適配器 self.mount(’http://’, HTTPAdapter()) # 根據schema來分配不同的適配器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 # 默認連接池DEFAULT_RETRIES = 0 # 默認重試次數DEFAULT_POOL_TIMEOUT = None # 超時時間

Python requests連接池是借用urllib3.poolmanager來實現的。

每一個獨立的(scheme, host, port)元祖使用同一個Connection, (scheme, host, port)是從請求的URL中解析分拆出來的。

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

下面是 urllib3的一些精簡源碼, 可以看出他的連接池實現也是簡單粗暴的。

# 解析url,分拆出scheme, host, portdef 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)# 獲取匹配的長連接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的長連接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)# 根據url的三個指標獲取連接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# 獲取長連接的主入口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關于session連接類實現,說的算明白了。 但就requests和urllib3的連接池實現來說,還是有一些提升空間的。 但問題來了,單單靠著域名和端口會造成一些問題,至于造成什么樣子的問題,我在上面已經有詳細的描述了。

那么如何解決?

我們可以用 scheme + 主domain + host_ip + port 來實現長連接池的管理。

其實大多數的場景是無需這么細致的實現連接池的,但根據我們的測試的結果來看,在服務初期性能提升還是不小的。

這樣既解決了域名ip輪詢帶來的連接重置問題,也解決了多級域名下不能共用連接的問題。

以上這篇構建高效的python requests長連接池詳解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持好吧啦網。

標簽: Python 編程
相關文章:
主站蜘蛛池模板: 喷砂机厂家_自动喷砂机生产_新瑞自动化喷砂除锈设备 | 环比机械| 上海办公室设计_办公楼,写字楼装修_办公室装修公司-匠御设计 | 百度爱采购运营研究社社群-店铺托管-爱采购代运营-良言多米网络公司 | 电渗析,废酸回收,双极膜-山东天维膜技术有限公司 | 世纪豪门官网 世纪豪门集成吊顶加盟电话 世纪豪门售后电话 | PE一体化污水处理设备_地埋式生活污水净化槽定制厂家-岩康塑业 | 除甲醛公司-甲醛检测治理-杭州创绿家环保科技有限公司-室内空气净化十大品牌 | 滚珠丝杆升降机_螺旋升降机_丝杠升降机-德迈传动 | 超高频感应加热设备_高频感应电源厂家_CCD视觉检测设备_振动盘视觉检测设备_深圳雨滴科技-深圳市雨滴科技有限公司 | 无压烧结银_有压烧结银_导电银胶_导电油墨_导电胶-善仁(浙江)新材料 | 玻璃钢板-玻璃钢防腐瓦-玻璃钢材料-广东壹诺 | 桨叶搅拌机_螺旋挤压/方盒旋切造粒机厂家-无锡市鸿诚输送机械有限公司 | 北京征地律师,征地拆迁律师,专业拆迁律师,北京拆迁律师,征地纠纷律师,征地诉讼律师,征地拆迁补偿,拆迁律师 - 北京凯诺律师事务所 | 钢格板|镀锌钢格板|热镀锌钢格板|格栅板|钢格板|钢格栅板|热浸锌钢格板|平台钢格板|镀锌钢格栅板|热镀锌钢格栅板|平台钢格栅板|不锈钢钢格栅板 - 专业钢格板厂家 | 信阳网站建设专家-信阳时代网联-【信阳网站建设百度推广优质服务提供商】信阳网站建设|信阳网络公司|信阳网络营销推广 | 金属回收_废铜废铁回收_边角料回收_废不锈钢回收_废旧电缆线回收-广东益夫金属回收公司 | 专注提供国外机电设备及配件-工业控制领域一站式服务商-深圳市华联欧国际贸易有限公司 | 高空重型升降平台_高空液压举升平台_高空作业平台_移动式升降机-河南华鹰机械设备有限公司 | 气动隔膜泵厂家-温州永嘉定远泵阀有限公司| 便携式谷丙转氨酶检测仪|华图生物科技百科 | 打包箱房_集成房屋-山东佳一集成房屋有限公司 | led太阳能路灯厂家价格_风光互补庭院灯_农村市政工程路灯-中山华可路灯品牌 | 电磁辐射仪-电磁辐射检测仪-pm2.5检测仪-多功能射线检测仪-上海何亦仪器仪表有限公司 | 沈阳庭院景观设计_私家花园_别墅庭院设计_阳台楼顶花园设计施工公司-【沈阳现代时园艺景观工程有限公司】 | 齿轮减速机电机一体机_齿轮减速箱加电机一体化-德国BOSERL蜗轮蜗杆减速机电机生产厂家 | 广东教师资格网-广东教师资格证考试网 | 数码听觉统合训练系统-儿童感觉-早期言语评估与训练系统-北京鑫泰盛世科技发展有限公司 | 常州律师事务所_常州律所_常州律师-江苏乐天律师事务所 | 西安中国国际旅行社(西安国旅) | 帽子厂家_帽子工厂_帽子定做_义乌帽厂_帽厂_制帽厂_帽子厂_浙江高普制帽厂 | 正压送风机-多叶送风口-板式排烟口-德州志诺通风设备 | 单螺旋速冻机-双螺旋-流态化-隧道式-食品速冻机厂家-广州冰泉制冷 | 写方案网_方案策划方案模板下载| 米顿罗计量泵(科普)——韬铭机械 | 洁净实验室工程-成都手术室净化-无尘车间装修-四川华锐净化公司-洁净室专业厂家 | 工业设计,人工智能,体验式3D展示的智能技术交流服务平台-纳金网 J.S.Bach 圣巴赫_高端背景音乐系统_官网 | 派财经_聚焦数字经济内容服务平台| 蔬菜配送公司|蔬菜配送中心|食材配送|饭堂配送|食堂配送-首宏公司 | IWIS链条代理-ALPS耦合透镜-硅烷预处理剂-上海顶楚电子有限公司 lcd条形屏-液晶长条屏-户外广告屏-条形智能显示屏-深圳市条形智能电子有限公司 | 芜湖厨房设备_芜湖商用厨具_芜湖厨具设备-芜湖鑫环厨具有限公司 控显科技 - 工控一体机、工业显示器、工业平板电脑源头厂家 |