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

在这里插入图片描述

缓冲区的作用

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

  • 提升效率:批量发送、批量读取(数据量较小轻松应对)
  • 拥塞机制:发送之前询问接收方是否处理有压力,发多少数据合适。(正常运行)
  • 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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值