Saber
⚔️ Saber, PHP异步协程HTTP客户端 | PHP Coroutine HTTP client - Swoole Humanization Library
Install / Use
/learn @swlib/SaberREADME
Saber
简介
HTTP军刀(呆毛王), Swoole人性化组件库之PHP高性能HTTP客户端, 基于Swoole原生协程, 支持多种风格操作, 底层提供高性能解决方案, 让开发者专注于功能开发, 从传统同步阻塞且配置繁琐的Curl中解放.
- 基于Swoole协程Client开发
- 人性化使用风格, ajax.js/axios.js/requests.py用户福音, 同时支持PSR风格操作
- 浏览器级别完备的Cookie管理机制, 完美适配爬虫/API代理应用
- 请求/响应/异常拦截器
- 多请求并发, 并发重定向优化
- 连接池, 自动化复用长连接
- 通道池(Chan): 最大连接数限制+无阻塞
- HTTPS连接, CA证书自动化支持
- HTTP/Socks5 Proxy支持
- WebSocket连接支持
- 毫秒级超时定时器
- 自动化 编码请求/解析响应 数据
- 响应报文自动编码转换
- 异步超大文件上传/下载, 断点重传
- 自动重试机制
- 单次并发数控制
- 多模式/超细粒度异常处理机制
- (=)浏览器级别缓存机制
- (=)随机UA生成器
<br>
安装
最好的安装方法是通过 Composer 包管理器 :
composer require swlib/saber
依赖
- PHP71 or later
- Swoole 2.1.2 or later
- Swoole 4 is the best
<br>
协程调度
Swoole底层实现协程调度, 业务层无需感知, 开发者可以无感知的用同步的代码编写方式达到异步IO的效果和超高性能,避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护.
需要在onRequet, onReceive, onConnect等事件回调函数中使用, 或是使用go关键字包裹 (swoole.use_shortname默认开启).
go(function () {
echo SaberGM::get('http://httpbin.org/get');
})
目录
- <a href="#例子">例子</a>
- <a href="#静态方法">静态方法</a>
- <a href="#生成实例">生成实例</a>
- <a href="#生成会话">生成会话</a>
- <a href="#并发请求">并发请求</a>
- <a href="#数据解析">数据解析</a>
- <a href="#网络代理">网络代理</a>
- <a href="#文件上传">文件上传</a>
- <a href="#超大文件下载">超大文件下载</a>
- <a href="#自动重试">自动重试</a>
- <a href="#缓存机制">缓存机制</a>
- <a href="#psr风格">PSR风格</a>
- <a href="#websocket">WebSocket</a>
- <a href="#极限压力测试">极限压力测试</a>
- <a href="#列式请求集">列式请求集</a>
- <a href="#单次并发控制">单次并发控制</a>
- <a href="#高性能无极限协程连接池">高性能无极限协程连接池</a>
- <a href="#无限连接池">无限连接池</a>
- <a href="#定容连接池">定容连接池</a>
- <a href="#动态变容">动态变容</a>
- <a href="#注意事项">注意事项</a>
- <a href="#注册你所希望的配置">注册你所希望的配置</a>
- <a href="#注意在一次性脚本中释放连接池">注意在一次性脚本中释放连接池</a>
- <a href="#注册你所希望的配置">注册你所希望的配置</a>
- <a href="#配置参数表">配置参数表</a>
- <a href="#配置参数别名">配置参数别名</a>
- <a href="#拦截器">拦截器</a>
- <a href="#cookies">Cookies</a>
- <a href="#属性">属性</a>
- <a href="#任意格式互转">任意格式互转</a>
- <a href="#域名路径和过期时限校验">域名路径和过期时限校验</a>
- <a href="#持久化存储">持久化存储</a>
- <a href="#异常机制">异常机制</a>
- <a href="#捕获例子">捕获例子</a>
- <a href="#异常报告级别控制">异常报告级别控制</a>
- <a href="#掩码表">掩码表</a>
- <a href="#异常自定义处理函数">异常自定义处理函数</a>
- <a href="#road-map">Road Map</a>
- <a href="#why-not-http2-">Why not Http2 ?</a>
- <a href="#ide-helper">IDE Helper</a>
- <a href="#重中之重">重中之重</a>
- <a href="#附录">附录</a>
- <a href="#saber-api">Saber API</a>
- <a href="#swlibsabergm">Swlib\SaberGM</a>
- <a href="#swlibsaber">Swlib\Saber</a>
- <a href="#swlibsaberrequest">Swlib\Saber\Request</a>
- <a href="#swlibsaberresponse">Swlib\Saber\Response</a>
- <a href="#swlibsaberrequestqueue">Swlib\Saber\RequestQueue</a>
- <a href="#swlibsaberresponsemap">Swlib\Saber\ResponseMap</a>
- <a href="#swlibsaberwebsocket">Swlib\Saber\WebSocket</a>
- <a href="#swlibsaberwebsocketframe">Swlib\Saber\WebSocketFrame</a>
- <a href="#saber-api">Saber API</a>
例子
静态方法
数据自动打包: 传入的data会自动转换成content-type所指定的类型格式
默认为
x-www-form-urlencoded, 也支持json等其它格式
SaberGM := Saber Global Manager, 如果觉得类名有点长, 可以使用class_alias自己取别名, 推荐服务中使用生成实例的方式使用, 而把SaberGM作为快捷方式.
SaberGM::get('http://httpbin.org/get');
SaberGM::delete('http://httpbin.org/delete');
SaberGM::post('http://httpbin.org/post', ['foo' => 'bar']);
SaberGM::put('http://httpbin.org/put', ['foo' => 'bar']);
SaberGM::patch('http://httpbin.org/patch', ['foo' => 'bar']);
生成实例
适用API代理服务
$saber = Saber::create([
'base_uri' => 'http://httpbin.org',
'headers' => [
'Accept-Language' => 'en,zh-CN;q=0.9,zh;q=0.8',
'Content-Type' => ContentType::JSON,
'DNT' => '1',
'User-Agent' => null
]
]);
echo $saber->get('/get');
echo $saber->delete('/delete');
echo $saber->post('/post', ['foo' => 'bar']);
echo $saber->patch('/patch', ['foo' => 'bar']);
echo $saber->put('/put', ['foo' => 'bar']);
生成会话
Session会自动保存cookie信息, 其实现是浏览器级别完备的
$session = Saber::session([
'base_uri' => 'http://httpbin.org',
'redirect' => 0
]);
$session->get('/cookies/set?foo=bar&k=v&apple=banana');
$session->get('/cookies/delete?k');
echo $session->get('/cookies')->body;
并发请求
注意: 此处使用了并发重定向优化方案, 多个重定向总是依旧并发的而不会退化为队列的单个请求
$responses = SaberGM::requests([
['uri' => 'http://github.com/'],
['uri' => 'http://github.com/'],
['uri' => 'https://github.com/']
]);
echo "multi-requests [ {$responses->success_num} ok, {$responses->error_num} error ]:\n" ."consuming-time: {$responses->time}s\n";
// multi-requests [ 3 ok, 0 error ]:
// consuming-time: 0.79090881347656s
// 别名机制可以省略参数书写参数名
$saber = Saber::create(['base_uri' => 'http://httpbin.org']);
echo $saber->requests([
['get','/get'],
['post','/post'],
['patch','/patch'],
['put','/put'],
['delete','/delete']
]);
数据解析
目前支持json,xml,html,url-query四种格式的数据快速解析
[$json, $xml, $html] = SaberGM::list([
'uri' => [
'http://httpbin.org/get',
'http://www.w3school.com.cn/example/xmle/note.xml',
'http://httpbin.org/html'
]
]);
var_dump($json->getParsedJsonArray());
var_dump($json->getParsedJsonObject());
var_dump($xml->getParsedXmlArray());
var_dump($xml->getParsedXmlObject(true));
var_dump($html->getParsedDomObject()->getElementsByTagName('h1')->item(0)->textContent);
网络代理
支持HTTP和SOCKS5代理
$uri = 'http://myip.ipip.net/';
echo SaberGM::get($uri, ['proxy' => 'http://127.0.0.1:1087'])->body;
echo SaberGM::get($uri, ['proxy' => 'socks5://127.0.0.1:1086'])->body;
文件上传
底层自动协程调度, 可支持异步发送超大文件, 断点续传
同时上传三个文件(三种参数风格
string|array|object)
$file1 = __DIR__ . '/black.png';
$file2 = [
'path' => __DIR__ . '/black.png',
'name' => 'white.png',
'type' => ContentType::MAP['png'],
'offset' => null, //re-upload from break
'size' => null //upload a part of the file
];
$file3 = new SwUploadFile(
__DIR__ . '/black.png',
'white.png',
ContentType::MAP['png']
);
echo SaberGM::post('http://httpbin.org/post', null, [
'files' => [
'image1' => $file1,
'image2' => $file2,
'image3' => $file3
]
]
);
超大文件下载
Download收到数据后会直接异步写入到磁盘, 而不是在内存中对HttpBody进行拼接. 因此download仅使用小量内存, 就可以完成超大文件的下载. 且支持断点续传, 通过设置offset参数来进行断点下载.
异步下载Saber壁纸
$download_dir = '/tmp/saber.jpg';
$response = SaberGM::download(
'https://ws1.sinaimg.cn/large/006DQdzWly1fsr8jt2botj31hc0wxqfs.jpg',
$download_dir
);
if ($response->success) {
exec('open ' . $download_dir);
}
自动重试
在爬虫项目中, 请求失败自动重试是非常常见的需求, 比如会话过期后重新登录.
而Saber内置了此功能, 并可使用拦截器来强化它.
如未设置retry_time而设置了retry拦截器, 则retry_time会置为1, 如retry拦截器的回调方法返回了false, 无论retry_time是多少, 都会在返回false时终止重试.
$uri = 'http://eu.httpbin.org/basic-auth/foo/bar';
$res = SaberGM::get(
$uri, [
'exception_report' => 0,
'retry_time' => 3,
'retry' => function (Saber\Request $request) {
echo "retry...\n";
$request->withBasicAuth('foo', 'bar'); //发现失败后添加验证信息
if ('i don not want to retry again') {
return false; // shutdown
}
}
]
);
echo $res;
缓存机制
有时候HTTP资源并不会总是变更, 我们可以学习浏览器缓存不会变动的资源, 来加快请求效率, 由Saber自动化地完成且不必自己维护缓存逻辑(CURD或文件读写), 协程的调度使得其不论如何都不会阻塞服务器, Saber没有使用中间件机制因为它和Swoole是强相关的, 但是缓存可以使用 内存/文件/数据库 等多种方式, 所以虽然它尚未实现, 但它将会列入Saber的后续路线图中.
PSR风格
$bufferStream = new BufferStream();
$bufferStream->write(json_encode(['foo' => 'bar']));
$response = SaberGM::psr()
->withMethod('POST')
->withUri(new Uri('http://httpbin.org/post?foo=bar'))
->withQueryParams(['foo' => 'option is higher-level than uri'])
->withHeader('content-type', ContentType::JSON)
->withBody($bufferStream)
->exec()->recv();
echo $response->getBody();
WebSocket
可以通过websocketFrame数据帧的__toString方法直接打印返回数据字符串
$websocket = SaberGM::websocket('ws://127.0.0.1:9999');
while (true) {
echo $websocket->recv(1) . "\n";
$websocket->push("hello");
co::sleep(1);
}
极限压力测试
测试机器为最低配MacBookPro, 请求服务器为本地echo服务器
0.9秒完成6666个请求, 成功率100%.
co::set(['max_coroutine' => 8191]);
go(function () {
$requests = [];
for ($i = 6666; $i--;) {
$requests[] = ['uri' => 'http://127.0.0.1'];
}
$res = SaberGM::requests($requests);
echo "use {$res->time}s\n";
echo "success: $res->success_num, error: $res->error_num";
});
// on MacOS
// use 0.91531705856323s
// success: 6666, error: 0
列式请求集
在实际项目中, 经常会存在使用URL列表来配置请求的情况, 因此提供了list方法来方便使用:
echo SaberGM::list([
'uri' => [
'https://www.qq.com/',
'https://www.baidu.com/',
'https://www.swoole.com/',
'http://httpbin.org/'
]
]);
单次并发控制
在实际爬虫项目中, 我们往往要限制单次并发请求数量以防被服务器防火墙屏蔽, 而一个max_co参数就可以轻松地解决这个问题, max_co会将请求根据上限量分批将请求压入队列并执行收包.
// max_co is the max number of concurrency request once, it's very useful to prevent server-waf limit.
$requests = array_fill(0, 10, ['uri' => 'https://www.qq.com/']);
echo SaberGM::requests($requests, ['max_co' => 5])->time."\n";
echo SaberGM::requests($requests, ['max_co' => 1])->time."\n";
<br>
高性能无极限协程连
Related Skills
node-connect
344.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
99.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
344.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
344.4kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
