长连接最大的问题就是连接失效。
redis/mysql等服务器为什么会限制最大连接数
内核角度,过多的TCP连接非常的不环保
首先,过多的内存占用,尤其是阻塞的情况下。
例如如下代码。
服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?php
$serv = new Swoole\Server("127.0.0.1", 6666, SWOOLE_BASE); $serv->set(array( 'worker_num' => 1, ));
$serv->on('connect', function ($serv, $fd) { var_dump("Client: Connect $fd"); });
$serv->on('receive', function ($serv, $fd, $reactor_id, $data) { var_dump(strlen($data)); sleep(100000); });
$serv->on('close', function ($serv, $fd) { });
$serv->start();
|
代码很简单,服务器在第一次收到数据的时候,就调用sleep
函数阻塞起来。(因为只有一个worker
进程,所以后面不会处理客户端发来的其他数据)
客户端代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php
use Swoole\Coroutine\Socket;
$socket = new Socket(AF_INET, SOCK_STREAM, 0);
go(function () use ($socket) { $socket->connect('127.0.0.1', 6666);
$i = 0; while ($i++ < 100000000) { var_dump($socket->send(str_repeat("1", 1024))); } echo "done\n"; });
|
代码很简单,就是创建一个协程,然后创建一个客户端,往服务器发送数据。一共发送了100000000 * 1024
字节的数据。
我们先启动服务器:
1 2
| [root@aaeb2c267d0f test]# php server.php
|
然后再启动客户端:
1 2 3 4 5 6 7 8 9
| [root@aaeb2c267d0f test]# php client.php # 省略其他的输出 int(1024) int(1024) int(1024) int(1024) int(1024) int(617)
|
我们发现,客户端程序会阻塞住。
我们再来看看服务器端这边的输出:
1 2 3 4
| [root@aaeb2c267d0f test]# php server.php string(17) "Client: Connect 1" int(6144)
|
我们发现,服务器读取到了6144
字节的数据之后,就没有继续再读取了。(我们发现,服务器端读取到的字节大小不一定是1024
,这也说明了TCP
是面向字节流的,每次调用send
函数,数据不一定就会立马发送过去。同理,服务器端也不会收到数据后就立马读取)
此时,我们关闭客户端这边的进程:
1 2
| ^C [root@aaeb2c267d0f test]#
|
然后查看socket
内核缓存区的状态:
1 2 3 4 5 6 7 8
| [root@aaeb2c267d0f test]# cat /proc/net/sockstat sockets: used 177 TCP: inuse 7 orphan 1 tw 0 alloc 15 mem 1211 UDP: inuse 1 mem 2 UDPLITE: inuse 0 RAW: inuse 0 FRAG: inuse 0 memory 0 [root@aaeb2c267d0f test]#
|
我们发现内核缓冲区是有数据没有被清空的。
所以我们发现,当服务器进程因为一个socket
而被阻塞起来了,那么后续的连接都无法被处理,导致内存占用严重。
其次,过多的端口占用、fd占用。我们来看一个TCP
自连接的问题。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php
use Swoole\Coroutine\Socket;
$socket = new Socket(AF_INET, SOCK_STREAM, 0);
go(function () use ($socket) { for ($i = 1; $i < 65536; $i++) { if ($socket->connect('127.0.0.1', $i)) { echo "connected\n"; break; } }
var_dump($i); });
|
执行结果如下:
1 2 3 4
| [root@aaeb2c267d0f test]# php client.php connected int(43991) [root@aaeb2c267d0f test]#
|
我们发现,如果我们不限制连接的个数,那么客户端很可能就会耗尽可用的端口。可用的端口范围可以通过如下命令查看:
1 2
| [root@aaeb2c267d0f test]# sysctl -a | grep ip_local_port_range net.ipv4.ip_local_port_range = 32768 60999
|
传输效率问题
因为长连接可以减少TCP
三次握手的时间,所以长连接的效率会高于短连接(短连接每次发送数据需要进行三次握手)。
综上,我们要控制长连接的数量,也就是说在限制了最大连接数的情况下,连接弥足珍贵。那我们确实有那么多进程怎么办,只能尽可能提高利用率,共享连接。
共享连接池
多协程共享连接
当协程需要使用一个连接的时候,可以从Channel
里面pop
出来一个连接。
多进程共享连接
跨机器共享连接