Python 同、异步HTTP客户端封装:性能与简洁性的较量
一、前言
- 引入异步编程趋势:Python的异步编程正变得越来越流行。在过去,同步的HTTP请求已经不足以满足对性能的要求。
- 异步HTTP客户端库的流行:目前,有许多第三方库已经实现了异步HTTP客户端,如aiohttp和httpx等。然而,异步语法使得代码变得更加冗长,导致缩进增多,降低了代码的可读性和简洁性。
- 封装异步HTTP客户端:为了简化异步HTTP请求的代码,我们需要封装一个常用的HTTP客户端,以实现业务中常见的功能,并提供更简洁的接口。在这篇博客中,我将使用httpx库来进行封装异步客户端,requests则是封装同步客户端,以实现常见的HTTP方法,并支持设置超时时间、请求参数等功能。
原文:Python 同、异步HTTP客户端封装:性能与简洁性的较量
二、同异步http客户端测试
同异步简易Demo
再封装之前先看看同异步发个http请求的代码差异,这里以 requests、aiohttp、httpx进行展示
依赖安装
pip install requests aiohttp httpx
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @Desc: { 模块描述 }
# @Date: 2023/09/28 10:09
import asyncio
import httpx
import aiohttp
import requestsdef requests_demo(url):print("requests_demo")resp = requests.get(url)print(resp.text)async def aiohttp_demo(url):print("aiohttp_demo")async with aiohttp.client.ClientSession() as session:async with session.get(url) as resp:html_text = await resp.text()print(html_text)async def httpx_demo(url):print("httpx_demo")async with httpx.AsyncClient() as client:resp = await client.get(url)print(resp.text)async def main():url = "https://juejin.cn/"requests_demo(url)await aiohttp_demo(url)await httpx_demo(url)if __name__ == '__main__':asyncio.run(main())
可以看到同步的requests库实现的非常简洁一行代码就可以发送http请求。但异步语法的 httpx与aiohttp就感觉代码很臃肿,要嵌套好多层,尤其aiohttp,可读性变差了好多,但异步的请求可以大大的提升并发性能,利用网络IO的耗时处理更多的请求任务,这在爬虫中可以大大提升性能,再异步的web框架中也非常适用。
并发http请求测试
再看看同异步如何并发请求数据
async def concurrent_http_test():# requests testurls = ["https://juejin.cn/"] * 10start_time = time.time()for url in urls:requests_demo(url)use_time = time.time() - start_timeprint(f"requests {len(urls)} http req use {use_time} s")# httpx teststart_time = time.time()await asyncio.gather(*[httpx_demo(url) for url in urls])use_time = time.time() - start_timeprint(f"httpx {len(urls)} http req use {use_time} s")# aiohttp teststart_time = time.time()await asyncio.gather(*[aiohttp_demo(url) for url in urls])use_time = time.time() - start_timeprint(f"aiohttp {len(urls)} http req use {use_time} s")
结果:
requests 10 http req use 2.9108400344848633 shttpx 10 http req use 0.8657052516937256 saiohttp 10 http req use 1.9703822135925293 s
requests 请求demo是同步一个一个请求,所以会慢好多,而 httpx、aiohttp 是通过 asyncio.gather 并发请求的,会一次性发送10个请求,这样网络IO的耗时就复用了,但发现 aiohttp 的效果不尽人意,与httpx的0.86s相差太大,都是异步库,不应该的,于是看看之前写的demo代码发现其实aiohttp并没有复用 ClientSession 每次都是创建一个新的实例来去发送请求,这样频繁的创建与销毁连接会大大影响性能,httpx的 async with httpx.AsyncClient() as client:
好像是一样的问题,但httpx效果更好些。
尝试把 aiohttp 的 ClientSession 与 httpx.AsyncClient() 放到全局中去,再试试。
def requests_demo(url, session):# print("requests_demo")resp = session.get(url)return respasync def aiohttp_demo(url, aio_session):# print("aiohttp_demo")async with aio_session.get(url) as resp:# html_text = await resp.text()return respasync def httpx_demo(url, client):# print("httpx_demo")resp = await client.get(url)return respasync def concurrent_http_test():# requests testurls = ["https://juejin.cn/"] * 10start_time = time.time()with ThreadPoolExecutor() as pool:session = requests.session()for url in urls:pool.submit(requests_demo, url, session)use_time = time.time() - start_timeprint(f"requests {len(urls)} http req use {use_time} s")# aiohttp teststart_time = time.time()async with aiohttp.client.ClientSession() as aio_session:await asyncio.gather(*[aiohttp_demo(url, aio_session) for url in urls])use_time = time.time() - start_timeprint(f"aiohttp {len(urls)} http req use {use_time} s")# httpx teststart_time = time.time()async with httpx.AsyncClient() as client:await asyncio.gather(*[httpx_demo(url, client) for url in urls])use_time = time.time() - start_timeprint(f"httpx {len(urls)} http req use {use_time} s")
改进效果
requests 10 http req use 1.2176601886749268 saiohttp 10 http req use 0.4052879810333252 shttpx 10 http req use 0.5238490104675293 s
异步的效果很明显快了很多,requests 请求我也用 session 与线程池来并发请求看看效果,但网络有波动每次测的数据都不一样,所以这里的测试值仅作为参考。
三、异步http客户端封装
简易封装
aiohttp 与 httpx 性能都差不多,由于之前用 requests 习惯了,再接触这些异步封装的语法都觉得好怪,而 httpx的api 与 requests 类似,所以我就选择用 htppx 简单封装下。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author: Hui
# @Desc: { http客户端 }
# @Date: 2023/08/10 09:33
import httpx
from datetime import timedeltaclass HttpMethod(BaseEnum):GET = "GET"POST = "POST"PATCH = "PATCH"PUT = "PUT"DELETE = "DELETE"HEAD = "HEAD"OPTIONS = "OPTIONS"class RespFmt(BaseEnum):"""http响应格式"""JSON = "json"BYTES = "bytes"TEXT = "text"class AsyncHttpClient:"""异步HTTP客户端通过httpx封装,实现了常见的HTTP方法,支持设置超时时间、请求参数等,简化了异步调用的层级缩进。Attributes:default_timeout: 默认请求超时时间,单位秒default_headers: 默认请求头字典default_resp_fmt: 默认响应格式jsonclient: httpx 异步客户端response: 每次实例请求的响应"""def __init__(self, timeout=timedelta(seconds=10), headers: dict = None, resp_fmt: RespFmt = RespFmt.JSON):"""构造异步HTTP客户端"""self.default_timeout = timeoutself.default_headers = headers or {}self.default_resp_fmt = resp_fmtself.client = httpx.AsyncClient()self.response: httpx.Response = Noneasync def _request(self,method: HttpMethod, url: str,params: dict = None, data: dict = None,timeout: timedelta = None, **kwargs):"""内部请求实现方法创建客户端会话,构造并发送HTTP请求,返回响应对象Args:method: HttpMethod 请求方法, 'GET', 'POST' 等url: 请求URLparams: 请求查询字符串参数字典data: 请求体数据字典timeout: 超时时间,单位秒kwargs: 其他关键字参数Returns:httpx.Response: HTTP响应对象"""timeout = timeout or self.default_timeoutheaders = self.default_headers or {}self.response = await self.client.request(method=method.value,url=url,params=params,data=data,headers=headers,timeout=timeout.total_seconds(),**kwargs)return self.responsedef _parse_response(self, resp_fmt: RespFmt = None):"""解析响应Args:resp_fmt: 响应格式Returns:resp Union[dict, bytes, str]"""resp_fmt = resp_fmt or self.default_resp_fmtresp_content_mapping = {RespFmt.JSON: self.json,RespFmt.BYTES: self.bytes,RespFmt.TEXT: self.text,}resp_func = resp_content_mapping.get(resp_fmt)return resp_func()def json(self):return self.response.json()def bytes(self):return self.response.contentdef text(self):return self.response.textasync def get(self, url: str, params: dict = None, timeout: timedelta = None, resp_fmt: RespFmt = None, **kwargs):"""GET请求Args:url: 请求URLparams: 请求查询字符串参数字典timeout: 请求超时时间,单位秒resp_fmt: 响应格式,默认None 使用实例对象的 default_resp_fmtReturns:resp => dict or bytes"""await self._request(HttpMethod.GET, url, params=params, timeout=timeout, **kwargs)return self._parse_response(resp_fmt)async def post(self, url: str, data: dict = None, timeout: timedelta = None, resp_fmt: RespFmt = None, **kwargs):"""POST请求Args:url: 请求URLdata: 请求体数据字典timeout: 请求超时时间,单位秒resp_fmt: 响应格式,默认None 使用实例对象的 default_resp_fmtReturns:resp => dict or bytes"""await self._request(HttpMethod.POST, url, data=data, timeout=timeout, **kwargs)return self._parse_response(resp_fmt)async def put(self, url: str, data: dict = None, timeout: timedelta = None, resp_fmt: RespFmt = None, **kwargs):"""PUT请求Args:url: 请求URLdata: 请求体数据字典timeout: 请求超时时间,单位秒resp_fmt: 响应格式,默认None 使用实例对象的 default_resp_fmtReturns:resp => dict"""await self._request(HttpMethod.PUT, url, data=data, timeout=timeout, **kwargs)return self._parse_response(resp_fmt)async def delete(self, url: str, data: dict = None, timeout: timedelta = None, resp_fmt: RespFmt = None, **kwargs):"""DELETE请求Args:url: 请求URLdata: 请求体数据字典timeout: 请求超时时间,单位秒resp_fmt: 响应格式,默认None 使用实例对象的 default_resp_fmtReturns:resp => dict"""await self._request(HttpMethod.DELETE, url, data=data, timeout=timeout, **kwargs)return self._parse_response(resp_fmt)
封装细节
这里封装就是简单的内部维护一个 httpx 的异步客户端,然后初始化一些默认的参数
- default_timeout: 默认请求超时时间,单位秒,默认10s
- default_headers: 默认请求头字典
- default_resp_fmt: 默认响应格式json
- client: httpx 异步客户端
- response: 每次实例请求的响应
class AsyncHttpClient:"""异步HTTP客户端"""def __init__(self, timeout=timedelta(seconds=10), headers: dict = None, resp_fmt: RespFmt = RespFmt.JSON):"""构造异步HTTP客户端"""self.default_timeout = timeoutself.default_headers = headers or {}self.default_resp_fmt = resp_fmtself.client = httpx.AsyncClient()self.response: httpx.Response = None
然后实现几个常用的请求,get、post、put、delete方法
async def post(self, url: str, data: dict = None, timeout: timedelta = None, resp_fmt: RespFmt = None, **kwargs):"""POST请求Args:url: 请求URLdata: 请求体数据字典timeout: 请求超时时间,单位秒resp_fmt: 响应格式,默认None 使用实例对象的 default_resp_fmtReturns:resp => dict or bytes"""await self._request(HttpMethod.POST, url, data=data, timeout=timeout, **kwargs)return self._parse_response(resp_fmt)
每个请求方法冗余了一些常用的参数字段,例如
-
params 查询字符串入参
-
data body入参
-
timeout: 请求超时时间,单位秒
-
resp_fmt: 响应格式,默认None 使用实例对象的 default_resp_fmt
- 默认json,一般我们http的数据交互都是使用 json了
-
**kwargs 预留其他关键字参数的入参
- 这样有助于有些参数没想到要设计但经常用,可以通过kwargs来弥补
其实 get、post、put、delete方法没做什么事,就是标记了下使用什么请求方法、参数,最终都是让 _request
方法处理。
async def _request(self,method: HttpMethod, url: str,params: dict = None, data: dict = None,timeout: timedelta = None, **kwargs
):"""内部请求实现方法创建客户端会话,构造并发送HTTP请求,返回响应对象Args:method: HttpMethod 请求方法, 'GET', 'POST' 等url: 请求URLparams: 请求查询字符串参数字典data: 请求体数据字典timeout: 超时时间,单位秒kwargs: 其他关键字参数Returns:httpx.Response: HTTP响应对象"""timeout = timeout or self.default_timeoutheaders = self.default_headers or {}self.response = await self.client.request(method=method.value,url=url,params=params,data=data,headers=headers,timeout=timeout.total_seconds(),**kwargs)return self.response
处理完再根据指定的响应格式进行解析
def _parse_response(self, resp_fmt: RespFmt = None):"""解析响应Args:resp_fmt: 响应格式Returns:resp Union[dict, bytes, str]"""resp_fmt = resp_fmt or self.default_resp_fmtresp_content_mapping = {RespFmt.JSON: self.json,RespFmt.BYTES: self.bytes,RespFmt.TEXT: self.text,}resp_func = resp_content_mapping.get(resp_fmt)return resp_func()def json(self):return self.response.json()def bytes(self):return self.response.contentdef text(self):return self.response.text
通过字典的方法来处理不同的解析格式,简化了 if elif
的操作,这里封装主要是将一些常用操作封装起来,让代码更简洁,当然也可以获取响应对象后,自己自由处理,最后看看封装后的使用Demo
from py_tools.connections.http import AsyncHttpClient
from py_tools.enums.http import RespFmtasync def httpx_demo(url):print("httpx_demo")async with httpx.AsyncClient() as client:resp = await client.get(url)# print(resp.text)return respasync def main():url = "https://juejin.cn/"resp_obj = await httpx_demo(url)resp_text = resp_obj.textresp_text = await AsyncHttpClient().get(url, resp_fmt=RespFmt.TEXT)if __name__ == '__main__':asyncio.run(main())
封装后简洁了许多,虽然方法有些冗余参数,但在业务中使用就不会出现好多嵌套的缩进,也牺牲了一些灵活性,因为只封装一些常用的请求操作,但一开始也想不全,只有在业务中不断的磨练,以及大家一起提建议贡献,才能慢慢的变得更好用。有时候适当的冗余封装也挺不错的。
四、同步http客户端
同步的其实 requests 已经够简洁了,没必要再封装了,这里为了统一公共库的调用,就二次封装下,思路还是跟异步的一样,有一点不一样的就是,get、post、put、delete方法返回的是 self 的引用,用于一些链式操作。一开始我想把异步的也变成链式调用,发现做不到,方法如果不await拿不到结果,返回的是 协程对象,所以一时半会弄不出来,就用了一个参数的方式来处理。
class HttpClient:"""同步HTTP客户端通过request封装,实现了常见的HTTP方法,支持设置超时时间、请求参数等,链式调用Examples:>>> HttpClient().get("http://www.baidu.com").text>>> HttpClient().get("http://www.google.com", params={"name": "hui"}).bytes>>> HttpClient().post("http://www.google.com", data={"name": "hui"}).jsonAttributes:default_timeout: 默认请求超时时间,单位秒default_headers: 默认请求头字典client: request 客户端response: 每次实例请求的响应"""def __init__(self, timeout=timedelta(seconds=10), headers: dict = None):"""构造异步HTTP客户端"""self.default_timeout = timeoutself.default_headers = headers or {}self.client = requests.session()self.response: requests.Response = Nonedef _request(self,method: HttpMethod, url: str,params: dict = None, data: dict = None,timeout: timedelta = None, **kwargs):"""内部请求实现方法创建客户端会话,构造并发送HTTP请求,返回响应对象Args:method: HttpMethod 请求方法, 'GET', 'POST' 等url: 请求URLparams: 请求查询字符串参数字典data: 请求体数据字典timeout: 超时时间,单位秒kwargs: 其他关键字参数Returns:httpx.Response: HTTP响应对象"""timeout = timeout or self.default_timeoutheaders = self.default_headers or {}self.response = self.client.request(method=method.value,url=url,params=params,data=data,headers=headers,timeout=timeout.total_seconds(),**kwargs)return self.response@propertydef json(self):return self.response.json()@propertydef bytes(self):return self.response.content@propertydef text(self):return self.response.textdef get(self, url: str, params: dict = None, timeout: timedelta = None, **kwargs):"""GET请求Args:url: 请求URLparams: 请求查询字符串参数字典timeout: 请求超时时间,单位秒Returns:self 自身对象实例"""self._request(HttpMethod.GET, url, params=params, timeout=timeout, **kwargs)return selfdef post(self, url: str, data: dict = None, timeout: timedelta = None, **kwargs):"""POST请求Args:url: 请求URLdata: 请求体数据字典timeout: 请求超时时间,单位秒Returns:self 自身对象实例"""self._request(HttpMethod.POST, url, data=data, timeout=timeout, **kwargs)return selfasync def put(self, url: str, data: dict = None, timeout: timedelta = None, **kwargs):"""PUT请求Args:url: 请求URLdata: 请求体数据字典timeout: 请求超时时间,单位秒Returns:self 自身对象实例"""self._request(HttpMethod.PUT, url, data=data, timeout=timeout, **kwargs)return selfasync def delete(self, url: str, data: dict = None, timeout: timedelta = None, **kwargs):"""DELETE请求Args:url: 请求URLdata: 请求体数据字典timeout: 请求超时时间,单位秒Returns:self 自身对象实例"""self._request(HttpMethod.DELETE, url, data=data, timeout=timeout, **kwargs)return self
五、源代码
源代码已上传到了Github,里面也有具体的使用Demo,欢迎大家一起体验、贡献。
HuiDBK/py-tools: 打造 Python 开发常用的工具,让Coding变得更简单 (github.com)
相关文章:
Python 同、异步HTTP客户端封装:性能与简洁性的较量
一、前言 引入异步编程趋势:Python的异步编程正变得越来越流行。在过去,同步的HTTP请求已经不足以满足对性能的要求。异步HTTP客户端库的流行:目前,有许多第三方库已经实现了异步HTTP客户端,如aiohttp和httpx等。然而…...

无代码赋能数字化,云表搭桥铺路链接“数据孤岛”
什么是信息孤岛 企业数字化转型过程中,信息孤岛是一个突出的问题。这种情况发生的原因是,企业内部使用了多种应用软件,时间一长,员工在不同的系统中积累了大量的企业数据资产。然而,由于这些系统之间的数据无法互通&am…...

无需公网IP,实现公网SSH远程登录MacOS【内网穿透】
目录 前言 1. macOS打开远程登录 2. 局域网内测试ssh远程 3. 公网ssh远程连接macOS 3.1 macOS安装配置cpolar 3.2 获取ssh隧道公网地址 3.3 测试公网ssh远程连接macOS 4. 配置公网固定TCP地址 4.1 保留一个固定TCP端口地址 4.2 配置固定TCP端口地址 5. 使用固定TCP端…...
网络爬虫学习笔记 1 HTTP基本原理
HTTP原理 ~~~~~ HTTP(Hyper Text Transfer Protocol,超文本传输协议)是一种使用最为广泛的网络请求方式,常见于在浏览器输入一个地址。 1. URI和URL URL(Universal Resource Locator,统一资源定位器&…...

113. 路径总和ii
力扣题目链接(opens new window) 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。 说明: 叶子节点是指没有子节点的节点。 示例: 给定如下二叉树,以及目标和 sum 22, 在路径总和题目的基础上&…...

百度APP iOS端包体积50M优化实践(六)无用方法清理
一、前言 百度APP包体积经过一期优化,如无用资源清理,无用类下线,Xcode编译相关优化,体积已经有了明显的减少。但是优化后APP包体积在iPhone11上仍有350M的空间占用。与此同时百度APP作为百度的旗舰APP,业务迭代非常多…...
MySQL了解视图View (视图篇 一)
视图View是什么? MySQL的视图是一种虚拟表,它是基于一个或多个表的查询结果构建而成的。视图并不实际存储数据,而是根据定义的查询逻辑动态生成结果。 ----------------------------------- 视图的特点: - 虚拟表:…...

使用applescript自动化trilium的数学公式环境
众所周知,trilium什么都好,就是对数学公式的支持以及markdown格式的导入导出功能太拉了,而最拉的时刻当属把这两个功能结合起来的时候:导入markdown文件之后,原来的数学公式全没了,需要一个一个手动用ctrlm…...

idea中maven项目打包成jar,报错没有主清单属性解决方法
使用idea自带的打包可能会出现一下问题 在pom.xml中引入下面的依赖,即可解决 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><executions&…...

Caddy Web服务器深度解析与对比:Caddy vs. Nginx vs. Apache
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...

基于PHP+MySQL的家教平台
摘要 设计和实现基于PHP的家教平台是一个复杂而令人兴奋的任务。这个项目旨在为学生、家长和教师提供一个便捷的在线学习和教授平台。本文摘要将概述这个项目的关键方面,包括用户管理、课程管理、支付处理、评价系统、通知系统和安全性。首先,我们将建立…...

吉利微型纯电,5 万元的快乐
熊猫骑士作为一款主打下层市场的迷你车型,吉利熊猫骑士剑指宝骏悦也,五菱宏光 MINI 等热门选手。 9 月 15 日,吉利熊猫骑士正式上市,售价为 5.39 万,限时优享价 4 .99 万元。价格和配置上对这个级别定位的战略车型有一…...
Gitee使用方法
Gitee是一个基于 Git 的代码托管和协作平台,具有免费、稳定等特点,并且能够与国内的Gitee社区、码云等服务相结合使用。 以下是使用Gitee的主要步骤: 注册账号:访问Gitee官网,点击“注册”按钮,填写注册信…...

前端适配笔记本缩放125%,150%导致页面错乱问题
由于前端在开发时使用的都是标准ui设计图,基本都是按照所以1920*1080, 而小屏幕笔记本由于分辨率高,所以导致的显示元素变小,因此很多笔记本的默认显示都是放大125%或者150%。 如果页面比较简单就让多余的空白单边扩展,…...

多线程的学习中篇下
volatile 关键字 volatile 能保证内存可见性 volatile 修饰的变量, 能够保证 “内存可见性” 示例代码: 运行结果: 当输入1(1是非O)的时候,但是t1这个线程并沿有结束循环, 同时可以看到,t2这个线程已经执行完了,而t1线程还在继续循环. 这个情况,就叫做内存可见性问题 ~~ 这…...
贪心算法-拼接字符串使得字典顺序最小问题
题目1 给定一个由字符串组成的数组strs,必须把所有字符串拼接起来,返回所有可能的拼接结果中,字典序最小的结果 思路:对数组排序,排序规则是对ab和ba的字符串进行比较大小,返回较小的顺序放到数组中最后将…...
Linux--互斥锁
一、与互斥锁相关api **互斥量(mutex)**从本质上来说是一把锁。在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量。对互斥量进行枷锁后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释…...
[2023.09.21]:源码已上传,供大家了解Rust Yew的前后端开发
这个资源是Rust的源代码压缩包,供大家了解Rust Yew的前后端开发。 资源中的代码非常简洁易懂,虽然离商用场景还有一段距离,但是涵盖了前端的组件搭建、事件通信和反向代理,以及后端的Restful API的路由、功能实现和数据库访问。此…...

时序分解 | Matlab实现CEEMD互补集合经验模态分解时间序列信号分解
时序分解 | Matlab实现CEEMD互补集合经验模态分解时间序列信号分解 目录 时序分解 | Matlab实现CEEMD互补集合经验模态分解时间序列信号分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现CEEMD互补集合经验模态分解时间序列信号分解 1.分解效果图 ࿰…...
linux缓存-利用缓存提高性能的编程技巧
目录 利用缓存提高性能的编程技巧 实现方式 利用缓存提高性能的编程技巧 利用GCC编译器对齐属性 __attribute__((__aligned__(n))),利用处理器的缓存提高程序的执行速度; 使变量的起始地址对齐到一级缓存行长度的整数倍;使结构体对齐到一级缓存行长度…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

客户案例 | 短视频点播企业海外视频加速与成本优化:MediaPackage+Cloudfront 技术重构实践
01技术背景与业务挑战 某短视频点播企业深耕国内用户市场,但其后台应用系统部署于东南亚印尼 IDC 机房。 随着业务规模扩大,传统架构已较难满足当前企业发展的需求,企业面临着三重挑战: ① 业务:国内用户访问海外服…...