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
。
给 18yt 打赏