swoole - 面向连接(tcp)的输出缓冲区

本文探讨了缓冲区在客户端和服务端的应用,通过实验验证了socket_buffer_size与buffer_output_size的作用及影响,强调了合理配置的重要性。

在这里插入图片描述

缓冲区的作用

下面是我个人总结的几种作用,希望对理解记忆有帮助。

  • 提升效率:批量发送、批量读取(数据量较小轻松应对)
  • 拥塞机制:发送之前询问接收方是否处理有压力,发多少数据合适。(正常运行)
  • IO差异: 快速输出但网络延迟,快速输入但读取延迟 (数据量偏大可以应对)
  • 预警:通过对缓冲区大小的控制,防止爆满内存(数据量超大困难应对)

预警

此篇重点讨论服务端和客户端两个参数,这两个参数个人认为可划分到预警作用中。

客户端:

  • socket_buffer_size

    先看看客户端此参数如何定义的:“ 包括socket底层操作系统缓存区、应用层接收数据内存缓存区、应用层发送数据内存缓冲区”。可能是因为客户端通常只向一个服务端发送、接收数据,相对简单,所以整合在了一起。

服务端:

  • buffer_output_size
    代表服务器单个worker操作(send)的缓冲区,单次最大发送的数据,最大占用内存worker_num * buffer_output_size。

  • socket_buffer_size
    代表服务端为单个客户端连接(socket套接字)分配的输出缓冲区大小,待发送的数据上限。最大占用内存max_connection * socket_buffer_size;

在这里插入图片描述

测试

客户端

<?php
/**
 * 客户端代码保持不变
 * 接收服务器多次发送过来的数据 采用异步客户端
 * 将package_max_length调到不受此项影响的大小
 */
$client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);

$client->set([
	'open_length_check' => true,
    'package_max_length' => 5*1024*1024,
    'package_length_type' => 'N',
    'package_length_offset' => 0,
    'package_body_offset' => 4,
	]);

$client->on("connect", function(swoole_client $cli) {
	echo "connecting\n";
    $cli->send("GET / HTTP/1.1\r\n\r\n");
});
$client->on("receive", function(swoole_client $cli, $data){
	if($data){
		$len = unpack('N',$data);
		$body = substr($data,4,$len[1]);
		var_dump(strlen($body));
	}
});
$client->on("error", function(swoole_client $cli){
    echo "error\n";
});
$client->on("close", function(swoole_client $cli){
    echo "Connection close\n";
});

$client->connect('127.0.0.1', 6001);

buffer_output_size

单次send()最大发送的数据

<?php
/**
 * 测试buffer_output_size
 * 将socket_buffer_size调到不受此项影响的大小
 * 单次发送2M<buffer_output_size,循环40次
 */
$serv = new swoole_server("127.0.0.1", 6001);
$serv->set([
	'worker_num'=>1,
    'buffer_output_size' => 3 * 1024 *1024,
    'socket_buffer_size' => 128 * 1024 *1024,
]);
$serv->on('connect', function (swoole_server $serv, int $fd) {

   	$body = str_repeat('a',2*1024*1024);
	$head = pack('N',strlen($body));
	$pack = $head.$body;

	for ($i=0; $i < 40; $i++) {
		$serv->send($fd,$pack);
	}
});
$serv->on('receive',function($serv, $fd, $reactor_id, $data){
	echo $data;
});
$serv->start();

结果:没有异常,客户端全部接收。说明buffer_output_size和调用send多少次无关,只要单词不超过即可。

//修改为单词发送4M>buffer_output_size
$body = str_repeat('a',4*1024*1024);

结果:
在这里插入图片描述

socket_buffer_size

单个客户端连接(socket套接字)分配的输出缓冲区大小,待发送的数据上限

/**
 * 测试socket_buffer_size
 * 将buffer_output_size调到不受此项影响的大小
 * 单次发送3M>socket_buffer_size,发送一次
 */
$serv = new swoole_server("127.0.0.1", 6001);

$serv->set([
	'worker_num'=>1,
    'buffer_output_size' => 5 * 1024 *1024,
    'socket_buffer_size' => 2 * 1024 *1024,
]);

$serv->on('connect', function (swoole_server $serv, int $fd) {

   	$body = str_repeat('a',3*1024*1024);
	$head = pack('N',strlen($body));
	$pack = $head.$body;

	for ($i=0; $i < 1; $i++) {
		$serv->send($fd,$pack);
	}
});

$serv->on('receive',function($serv, $fd, $reactor_id, $data){
	echo $data;
});

$serv->start();

结果:没有异常,客户端正常接收数据。明明发送的3M大于socket_buffer_size的2M,为什么还能发送成功呢?

//调大循环发送次数 经测试在循环4次时提示缓存溢出
for ($i=0; $i < 4; $i++) {
		$serv->send($fd,$pack);
}

在这里插入图片描述
这说明缓冲区像一个漏桶,即使大于漏桶体积的水进来,可能也不会溢出,但多次累积则造成溢出。那么如果小于漏桶体积的水进来,能保证不会溢出吗?

//修改单次发送1M (<socket_buffer_size=2M)
$body = str_repeat('a',1*1024*1024);
//循环发送10次
for ($i=0; $i < 10; $i++) {
		$serv->send($fd,$pack);
}

为了模仿客户端阻塞,修改客户端代码 阻塞1毫秒
$client->on("receive", function(swoole_client $cli, $data){
	usleep(1000);
	....

结果:
在这里插入图片描述
客户端成功接收8次后,输出缓存溢出。
在这里插入图片描述

总结

socket_buffer_size即使设置的足够大,也有可能溢出。
解决办法可通过buffer_output_size限制单次send()大小,最可靠的办法是不能一味的发送,而是得到客户端的响应后再继续发送。
socket_buffer_size不能设置的过大,此选项是单个套接字连接占用的内存,如果是1W个客户端连接,则总占用内存数1W*socket_buffer_size

在编译 Swoole 扩展时遇到 `configure failed` 错误,通常与环境依赖、配置参数兼容性或 MAMP 的特殊路径设置有关。以下是一些常见的原因及解决方法: ### 检查 PHP 版本与 Swoole 的兼容性 PHP 8.1.13 属于较新版本,而某些 Swoole 功能模块(如 `swoole_thread` 或 `iouring`)可能尚未完全适配该版本的 Zend 引擎特性或内存管理机制。建议确认当前使用的 Swoole 版本是否支持 PHP 8.1,特别是启用 `enable-swoole-thread` 和 `enable-iouring` 等实验性选项时[^1]。 可以尝试使用稳定版本的 Swoole(例如 v4.8.x 或 v5.0.x),并确保从官方 GitHub 仓库克隆源码后切换到对应分支或标签: ```bash git clone https://github.com/swoole/swoole-src.git cd swoole-src git checkout v5.0.0 ``` ### 配置参数冲突或依赖缺失 启用多个扩展选项(如 `--enable-sockets`, `--enable-swoole-curl`, `--enable-swoole-pgsql`, `--enable-brotli`, `--enable-zstd`, `--enable-swoole-thread`, `--enable-iouring`)可能导致依赖库未正确安装或配置冲突。例如: - `--enable-swoole-pgsql` 需要 PostgreSQL 开发库(如 `libpq-dev`) - `--enable-brotli` 和 `--enable-zstd` 分别需要 Brotli 和 Zstandard 库的头文件和开发包 - `--enable-swoole-thread` 要求系统支持 pthread,并且部分函数签名可能与 PHP 8.1 不兼容 - `--enable-iouring` 需要 Linux 内核版本 >= 5.1 及 liburing 开发库 请检查系统中是否已安装这些依赖库,并根据提示修正配置命令。 ### MAMP 环境下的路径问题 MAMP 使用的是自定义的 PHP 安装路径,而非系统默认路径。因此,在配置 Swoole 时必须明确指定 MAMP 的 PHP 配置工具 `phpize` 和 `php-config`,否则会导致头文件路径错误或编译器参数不匹配。例如: ```bash export PATH=/Applications/MAMP/bin/php/php8.1.13/bin:$PATH /Applications/MAMP/bin/php/php8.1.13/bin/phpize ./configure --with-php-config=/Applications/MAMP/bin/php/php8.1.13/bin/php-config \ --enable-sockets \ --enable-swoole-curl \ --enable-swoole-pgsql \ --enable-brotli \ --enable-zstd \ --enable-swoole-thread \ --enable-iouring make clean && make && sudo make install ``` 此外,MAMP 的 PHP 构建可能缺少某些默认扩展头文件(如 `ext/sockets` 或 `ext/curl`),需手动安装相关开发包或复制头文件。 ### 编译日志分析 若上述步骤未能解决问题,应查看详细的 `config.log` 文件以定位具体失败原因。该文件通常位于 Swoole 源码目录下,其中会记录 configure 脚本执行期间的错误信息,包括缺失的头文件、链接失败的符号等关键线索。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值