百科.dev
全部条目AI 编程趋势榜开源项目技术资讯提交条目
登录
< 返回工具列表
S

saber

> 开发工具
开源

⚔️ Saber, PHP 同步协程 HTTP 客户端 | PHP Coroutine HTTP client - Swoole 人性化库

973 stars0 点赞1 次浏览
访问官网GitHub

工具介绍

⚔️ Saber, PHP 同步协程 HTTP 客户端 | PHP Coroutine HTTP client - Swoole 人性化库

Saber

简介

HTTP军刀(呆毛王), Swoole人性化组件库之PHP高性能HTTP客户端, 基于Swoole原生协程, 支持多种风格操作, 底层提供高性能解决方案, 让开发者专注于功能开发, 从传统同步阻塞且配置繁琐的Curl中解放.

English Document

  • 基于Swoole协程Client开发
  • 人性化使用风格, ajax.js/axios.js/requests.py用户福音, 同时支持PSR风格操作
  • 浏览器级别完备的Cookie管理机制, 完美适配爬虫/API代理应用
  • 请求/响应/异常拦截器
  • 多请求并发, 并发重定向优化
  • 连接池, 自动化复用长连接
  • 通道池(Chan): 最大连接数限制+无阻塞
  • HTTPS连接, CA证书自动化支持
  • HTTP/Socks5 Proxy支持
  • WebSocket连接支持
  • 毫秒级超时定时器
  • 自动化 编码请求/解析响应 数据
  • 响应报文自动编码转换
  • 异步超大文件上传/下载, 断点重传
  • 自动重试机制
  • 单次并发数控制
  • 多模式/超细粒度异常处理机制
  • (=)浏览器级别缓存机制
  • (=)随机UA生成器

安装

最好的安装方法是通过 Composer 包管理器 :

composer require swlib/saber

依赖

  • PHP71 or later
  • Swoole 2.1.2 or later
  • Swoole 4 is the best

协程调度

Swoole底层实现协程调度, 业务层无需感知, 开发者可以无感知的用同步的代码编写方式达到异步IO的效果和超高性能,避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护.

需要在onRequet, onReceive, onConnect等事件回调函数中使用, 或是使用go关键字包裹 (swoole.use_shortname默认开启).

go(function () {
    echo SaberGM::get('http://httpbin.org/get');
})

目录

  • 例子
    • 静态方法
    • 生成实例
    • 生成会话
    • 并发请求
    • 数据解析
    • 网络代理
    • 文件上传
    • 超大文件下载
    • 自动重试
    • 缓存机制
    • PSR风格
    • WebSocket
    • 极限压力测试
    • 列式请求集
    • 单次并发控制
    • 高性能无极限协程连接池
      • 无限连接池
      • 定容连接池
      • 动态变容
  • 注意事项
    • 注册你所希望的配置
      • 注意在一次性脚本中释放连接池
  • 配置参数表
    • 配置参数别名
  • 拦截器
  • Cookies
    • 属性
    • 任意格式互转
    • 域名路径和过期时限校验
    • 持久化存储
  • 异常机制
    • 捕获例子
    • 异常报告级别控制
      • 掩码表
    • 异常自定义处理函数
  • Road Map
    • Why not Http2 ?
  • IDE Helper
  • 重中之重
  • 附录
    • Saber API
      • Swlib\SaberGM
      • Swlib\Saber
      • Swlib\Saber\Request
      • Swlib\Saber\Response
      • Swlib\Saber\RequestQueue
      • Swlib\Saber\ResponseMap
      • Swlib\Saber\WebSocket
      • Swlib\Saber\WebSocketFrame

例子

静态方法

数据自动打包: 传入的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";

高性能无极限协程连接池

在常驻内存的服务器中使用时, 一定要手动开启连接池选项:

$swoole = Saber::create([
    'base_uri' => 'https://www.swoole.com/',
    'use_pool' => true
]);

在通过该实例使用时, 就会启用连接池特性, 即底层与www.swoole.com网站的连接客户端将会用一个全局连接池存取, 避免了每次使用创建/连接的开销.

无限连接池

在参数为true时, 该网站的连接池容量是无限的, 一般情况下没有问题, 且无限容量的连接池性能更好.

定容连接池

但如果你使用其作为爬虫代理服务, 遭遇大量请求时, 连接池中的客户端数量就会不可控制地快速上升, 甚至超出你所请求的源网站的最大允许连接数, 这时候你就需要将use_pool设置为一个理想数值(int), 此时, 底层会使用Channel作为连接池, 在连接池创建的客户端超出数量且不够取用时, 挂起需要取用客户端的协程, 并等待正在使用客户端的协程归还客户端, 协程等待和切换几乎没有多大的性能消耗, 是一种非常先进的解决方式.

动态变容

需要注意的是, 连接池是绑定服务器IP+端口的, 即如果你有多个实例面向的是同一个服务器IP+端口, 他们之间使用的连接池也是同一个.

所以你在重复创建服务器IP+端口的实例时, 新创建的实例指定的use_pool是允许覆盖之前数值的, 即连接池底层是自动变容的, 容量增加时底层会重新创建新的连接池并转移客户端, 容量减少时也会销毁在连接池内的多余的客户端.

注意事项

注册你所希望的配置

除了一定要记得配备连接池以外, 异常处理的方式也需要注意是符合你的编程习惯的, Saber默认的异常处理是最主流且严谨的抛出异常, 但Saber也支持静默地使用错误码和状态位, 可能更符合很多人的口味.

SaberGM::exceptionReport(0); // 关闭抛出异常报告, 在业务代码之前注册即可全局生效
$saber->exceptionReport(0);  //也可以单独设置某个实例

同理, 你所希望的配置都可以在业务代码之前如onWorkerStart甚至是swoole_server启动之前预先配置.

SaberGM::default([
    'exception_report' => 0
    'use_pool' => true
]);

像这样配置你所期望的选项可以让你获得更好的使用体验!

注意在一次性脚本中释放连接池

go(function(){
    // your code with pool...
    saber_pool_release(); // and this script will exit
});

如果你在一次性脚本中使用的连接池, 由于协程客户端是存在池中的, 引用计数为1无法释放, 就会导致swoole一直处于事件循环中, 脚本就无法退出, 你需要手动调用saber_pool_release或saber_exit或swoole_event_exit来正常退出, 也可以使用exit强制退出当前脚本(不要在server中使用exit).


配置参数表

|符号分割多种可选值

key type introduction example remark
protocol_version string HTTP协议版本 1.1 HTTP2还在规划中
base_uri string 基础路径 http://httpbin.org 将会与uri按照rfc3986合并
uri string 资源标识符 http://httpbin.org/get | /get | get 可以使用绝对路径和相对路径
uri_query string|array 请求信息 ['foo' => 'bar'] 非字符串会自动转换
method string 请求方法 get | post | head | patch | put | delete 底层自动转换为大写
headers array 请求报头 ['DNT' => '1'] | ['accept' => ['text/html'], ['application/xml']] 字段名不区分大小写, 但会保留设定时的原始大小写规则, 底层每个字段值会根据PSR-7自动分割为数组
cookies array|string ['foo '=> 'bar'] | 'foo=bar; foz=baz' 底层自动转化为Cookies对象, 并设置其domain为当前的uri, 具有浏览器级别的完备属性.
useragent string 用户代理 curl-1.0 默认为macos平台的chrome
referer string 来源地址 https://www.google.com 默认为空
redirect int 最大重定向次数 5 默认为3, 为0时不重定向.
keep_alive bool

Issues· 0 开放

查看全部 Issues在 GitHub 打开

暂无开放 Issues,或尚未同步最近议题。

> 标签

PHPajaxasyncaxioscoroutine

暂无评论,来聊聊你的看法吧

> 工具信息

发布日期2026年8月1日
最后更新2026年9月17日
分类开发工具
定价开源

> 相关工具

V
VS Code
流行的开源代码编辑器
G
Git
分布式版本控制系统
V
Vite
下一代前端构建工具