热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

php长连接心跳_PHPSwoole长连接常见问题总结

连接失效问题例子其中,Redis常见的报错就是:配置项:timeout报错信息:Errorwhilereadinglinef

连接失效问题

例子

其中,Redis常见的报错就是:

配置项:timeout

报错信息:Error while reading line from the server

Redis可以配置如果客户端经过多少秒还不给Redis服务器发送数据,那么就会把连接close掉。

更多学习内容请访问:八重樱:怎么从一名码农成为架构师的必看知识点:目录大全(不定期更新)​zhuanlan.zhihu.com

我的官方群点击此处。

swoole学习推荐视频

PHP—swoole通往大神修炼之路:av77924246

手把手教你用swoole+websocket实现户外监控直播(总集篇):av79087951

教你用swoole开发网络游戏:av79264440

PHP高级技术手写swoole分布式框架:av78383962

PHP高级技术手写swoole分布式框架(二):av78632435

PHP高级技术手写swoole分布式框架(三):av78748923

PHP高级技术手写swoole分布式框架(框架优化):av78856427

PHP高级技术手写swoole分布式框架(分布式RPC):av79012272

用swoole实现消息推送:av79874641

swoole+docker+redis主从复制及读写分离av78781841

MySQL常见的报错:

配置项:wait_timeout & interactive_timeout

报错信息:has gone away

和Redis服务器一样,MySQL也会定时的去清理掉没用的连接。

如何解决

1、用的时候进行重连

2、定时发送心跳维持连接

用的时候进行重连

优点是简单,缺点是面临短连接的问题。

定时发送心跳维持连接

推荐。

如何维持长连接

tcp协议中实现的tcp_keepalive

操作系统底层提供了一组tcp的keepalive配置:

tcp_keepalive_time (integer; default: 7200; since Linux 2.2)

The number of seconds a connection needs to be idle before TCP

begins sending out keep-alive probes. Keep-alives are sent only

when the SO_KEEPALIVE socket option is enabled. The default

value is 7200 seconds (2 hours). An idle connection is

terminated after approximately an additional 11 minutes (9

probes an interval of 75 seconds apart) when keep-alive is

enabled.

Note that underlying connection tracking mechanisms and

application timeouts may be much shorter.

tcp_keepalive_intvl (integer; default: 75; since Linux 2.4)

The number of seconds between TCP keep-alive probes.

tcp_keepalive_probes (integer; default: 9; since Linux 2.2)

The maximum number of TCP keep-alive probes to send before

giving up and killing the connection if no response is obtained

from the other end.

8

Swoole底层把这些配置开放出来了,例如:

?php

$server = new \Swoole\Server('127.0.0.1', 6666, SWOOLE_PROCESS);

$server->set([

'worker_num' => 1,

'open_tcp_keepalive' => 1,

'tcp_keepidle' => 4, // 对应tcp_keepalive_time

'tcp_keepinterval' => 1, // 对应tcp_keepalive_intvl

'tcp_keepcount' => 5, // 对应tcp_keepalive_probes

]);

其中:

'open_tcp_keepalive' => 1, // 总开关,用来开启tcp_keepalive

'tcp_keepidle' => 4, // 4s没有数据传输就进行检测

// 检测的策略如下:

'tcp_keepinterval' => 1, // 1s探测一次,即每隔1s给客户端发一个包(然后客户端可能会回一个ack的包,如果服务端收到了这个ack包,那么说明这个连接是活着的)

'tcp_keepcount' => 5, // 探测的次数,超过5次后客户端还没有回ack包,那么close此连接

我们来实战测试体验一下,服务端脚本如下:

$server = new \Swoole\Server('127.0.0.1', 6666, SWOOLE_PROCESS);

$server->set([

'worker_num' => 1,

'open_tcp_keepalive' => 1, // 开启tcp_keepalive

'tcp_keepidle' => 4, // 4s没有数据传输就进行检测

'tcp_keepinterval' => 1, // 1s探测一次

'tcp_keepcount' => 5, // 探测的次数,超过5次后还没有回包close此连接

]);

$server->on('connect', function ($server, $fd) {

var_dump("Client: Connect $fd");

});

$server->on('receive', function ($server, $fd, $reactor_id, $data) {

var_dump($data);

});

$server->on('close', function ($server, $fd) {

var_dump("close fd $fd");

});

$server->start();

我们启动这个服务器:

~/codeDir/phpCode/hyperf-skeleton # php server.php

然后通过tcpdump进行抓包:

~/codeDir/phpCode/hyperf-skeleton # tcpdump -i lo port 6666

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode

listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes

我们此时正在监听lo上的6666端口的数据包。

然后我们用客户端去连接它:

~/codeDir/phpCode/hyperf-skeleton # nc 127.0.0.1 6666

此时服务端会打印出消息:

~/codeDir/phpCode/hyperf-skeleton # php server.php

string(17) "Client: Connect 1"

tcpdump的输出信息如下:

01:48:40.178439 IP localhost.33933 > localhost.6666: Flags [S], seq 43162537, win 43690, options [mss 65495,sackOK,TS val 9833698 ecr 0,nop,wscale 7], length 0

01:48:40.178484 IP localhost.6666 > localhost.33933: Flags [S.], seq 1327460565, ack 43162538, win 43690, options [mss 65495,sackOK,TS val 9833698 ecr 9833698,nop,wscale 7], length 0

01:48:40.178519 IP localhost.33933 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 9833698 ecr 9833698], length 0

01:48:44.229926 IP localhost.6666 > localhost.33933: Flags [.], ack 1, win 342, options [nop,nop,TS val 9834104 ecr 9833698], length 0

01:48:44.229951 IP localhost.33933 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 9834104 ecr 9833698], length 0

01:48:44.229926 IP localhost.6666 > localhost.33933: Flags [.], ack 1, win 342, options [nop,nop,TS val 9834104 ecr 9833698], length 0

01:48:44.229951 IP localhost.33933 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 9834104 ecr 9833698], length 0

01:48:44.229926 IP localhost.6666 > localhost.33933: Flags [.], ack 1, win 342, options [nop,nop,TS val 9834104 ecr 9833698], length 0

// 省略了其他的输出

我们会发现最开始的时候,会打印三次握手的包:

01:48:40.178439 IP localhost.33933 > localhost.6666: Flags [S], seq 43162537, win 43690, options [mss 65495,sackOK,TS val 9833698 ecr 0,nop,wscale 7], length 0

01:48:40.178484 IP localhost.6666 > localhost.33933: Flags [S.], seq 1327460565, ack 43162538, win 43690, options [mss 65495,sackOK,TS val 9833698 ecr 9833698,nop,wscale 7], length 0

01:48:40.178519 IP localhost.33933 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 9833698 ecr 9833698], length 0

然后,停留了4s没有任何包的输出。

之后,每隔1s左右就会打印出一组:

01:52:54.359341 IP localhost.6666 > localhost.43101: Flags [.], ack 1, win 342, options [nop,nop,TS val 9859144 ecr 9858736], length 0

01:52:54.359377 IP localhost.43101 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 9859144 ecr 9855887], length 0

其实这就是我们配置的策略:

'tcp_keepinterval' => 1, // 1s探测一次

'tcp_keepcount' => 5, // 探测的次数,超过5次后还没有回包close此连接

因为我们操作系统底层会自动的给客户端回ack,所以这个连接不会在5次探测后被关闭。操作系统底层会持续不断的发送这样的一组包:

01:52:54.359341 IP localhost.6666 > localhost.43101: Flags [.], ack 1, win 342, options [nop,nop,TS val 9859144 ecr 9858736], length 0

01:52:54.359377 IP localhost.43101 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 9859144 ecr 9855887], length 0

如果我们要测试5次探测后关闭这个连接,可以禁掉6666端口的包:

~/codeDir/phpCode/hyperf-skeleton # iptables -A INPUT -p tcp --dport 6666 -j DROP

这样会把所有从6666端口进来的包给禁掉,自然,服务器就接收不到从客户端那一边发来的ack包了。

然后服务器过5秒就会打印出close(服务端主动的调用了close方法,给客户端发送了FIN包):

~/codeDir/phpCode/hyperf-skeleton # php server.php

string(17) "Client: Connect 1"

string(10) "close fd 1"

我们恢复一下iptables的规则:

~/codeDir/phpCode # iptables -D INPUT -p tcp -m tcp --dport 6666 -j DROP

即把我们设置的规则给删除了。

通过tcp_keepalive的方式实现心跳的功能,优点是简单,不要写代码就可以完成这个功能,并且发送的心跳包小。缺点是依赖于系统的网络环境,必须保证服务器和客户端都实现了这样的功能,需要客户端配合发心跳包。

还有一个更为严重的缺点是如果客户端和服务器不是直连的,而是通过代理来进行连接的,例如socks5代理,它只会转发应用层的包,不会转发更为底层的tcp探测包,那这个心跳功能就失效了。

所以,Swoole就提供了其他的解决方案,一组检测死连接的配置。

'heartbeat_check_interval' => 1, // 1s探测一次

'heartbeat_idle_time' => 5, // 5s未发送数据包就close此连接

swoole实现的heartbeat

我们来测试一下:

$server = new \Swoole\Server('127.0.0.1', 6666, SWOOLE_PROCESS);

$server->set([

'worker_num' => 1,

'heartbeat_check_interval' => 1, // 1s探测一次

'heartbeat_idle_time' => 5, // 5s未发送数据包就close此连接

]);

$server->on('connect', function ($server, $fd) {

var_dump("Client: Connect $fd");

});

$server->on('receive', function ($server, $fd, $reactor_id, $data) {

var_dump($data);

});

$server->on('close', function ($server, $fd) {

var_dump("close fd $fd");

});

$server->start();

然后启动服务器:

~/codeDir/phpCode/hyperf-skeleton # php server.php

然后启动tcpdump:

~/codeDir/phpCode # tcpdump -i lo port 6666

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode

listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes

然后再启动客户端:

~/codeDir/phpCode/hyperf-skeleton # nc 127.0.0.1 6666

此时服务器端打印:

~/codeDir/phpCode/hyperf-skeleton # php server.php

string(17) "Client: Connect 1"

然后tcpdump打印:

02:48:32.516093 IP localhost.42123 > localhost.6666: Flags [S], seq 1088388248, win 43690, options [mss 65495,sackOK,TS val 10193342 ecr 0,nop,wscale 7], length 0

02:48:32.516133 IP localhost.6666 > localhost.42123: Flags [S.], seq 80508236, ack 1088388249, win 43690, options [mss 65495,sackOK,TS val 10193342 ecr 10193342,nop,wscale 7], length 0

02:48:32.516156 IP localhost.42123 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 10193342 ecr 10193342], length 0

这是三次握手信息。

然后过了5s后,tcpdump会打印出:02:48:36.985027 IP localhost.6666 > localhost.42123: Flags [F.], seq 1, ack 1, win 342, options [nop,nop,TS val 10193789 ecr 10193342], length 0

02:48:36.992172 IP localhost.42123 > localhost.6666: Flags [.], ack 2, win 342, options [nop,nop,TS val 10193790 ecr 10193789], length 0

也就是服务端发送了FIN包。因为客户端没有发送数据,所以Swoole关闭了连接。

然后服务器端会打印:

~/codeDir/phpCode/hyperf-skeleton # php server.php

string(17) "Client: Connect 1"

string(10) "close fd 1"

所以,heartbeat和tcp keepalive还是有一定的区别的,tcp keepalive有保活连接的功能,但是heartbeat存粹是检测没有数据的连接,然后关闭它,并且只可以在服务端这边配置,如果需要保活,也可以让客户端配合发送心跳。

如果我们不想让服务端close掉连接,那么就得在应用层里面不断的发送数据包来进行保活,例如我在nc客户端里面不断的发送包:

~/codeDir/phpCode/hyperf-skeleton # nc 127.0.0.1 6666

ping

ping

ping

ping

ping

ping

ping

ping

ping

我发送了9个ping包给服务器,tcpdump的输出如下:

// 省略了三次握手的包

02:57:53.697363 IP localhost.44195 > localhost.6666: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 10249525 ecr 10249307], length 5

02:57:53.697390 IP localhost.6666 > localhost.44195: Flags [.], ack 6, win 342, options [nop,nop,TS val 10249525 ecr 10249525], length 0

02:57:55.309532 IP localhost.44195 > localhost.6666: Flags [P.], seq 6:11, ack 1, win 342, options [nop,nop,TS val 10249686 ecr 10249525], length 5

02:57:55.309576 IP localhost.6666 > localhost.44195: Flags [.], ack 11, win 342, options [nop,nop,TS val 10249686 ecr 10249686], length 0

02:57:58.395206 IP localhost.44195 > localhost.6666: Flags [P.], seq 11:16, ack 1, win 342, options [nop,nop,TS val 10249994 ecr 10249686], length 5

02:57:58.395239 IP localhost.6666 > localhost.44195: Flags [.], ack 16, win 342, options [nop,nop,TS val 10249994 ecr 10249994], length 0

02:58:01.858094 IP localhost.44195 > localhost.6666: Flags [P.], seq 16:21, ack 1, win 342, options [nop,nop,TS val 10250341 ecr 10249994], length 5

02:58:01.858126 IP localhost.6666 > localhost.44195: Flags [.], ack 21, win 342, options [nop,nop,TS val 10250341 ecr 10250341], length 0

02:58:04.132584 IP localhost.44195 > localhost.6666: Flags [P.], seq 21:26, ack 1, win 342, options [nop,nop,TS val 10250568 ecr 10250341], length 5

02:58:04.132609 IP localhost.6666 > localhost.44195: Flags [.], ack 26, win 342, options [nop,nop,TS val 10250568 ecr 10250568], length 0

02:58:05.895704 IP localhost.44195 > localhost.6666: Flags [P.], seq 26:31, ack 1, win 342, options [nop,nop,TS val 10250744 ecr 10250568], length 5

02:58:05.895728 IP localhost.6666 > localhost.44195: Flags [.], ack 31, win 342, options [nop,nop,TS val 10250744 ecr 10250744], length 0

02:58:07.150265 IP localhost.44195 > localhost.6666: Flags [P.], seq 31:36, ack 1, win 342, options [nop,nop,TS val 10250870 ecr 10250744], length 5

02:58:07.150288 IP localhost.6666 > localhost.44195: Flags [.], ack 36, win 342, options [nop,nop,TS val 10250870 ecr 10250870], length 0

02:58:08.349124 IP localhost.44195 > localhost.6666: Flags [P.], seq 36:41, ack 1, win 342, options [nop,nop,TS val 10250990 ecr 10250870], length 5

02:58:08.349156 IP localhost.6666 > localhost.44195: Flags [.], ack 41, win 342, options [nop,nop,TS val 10250990 ecr 10250990], length 0

02:58:09.906223 IP localhost.44195 > localhost.6666: Flags [P.], seq 41:46, ack 1, win 342, options [nop,nop,TS val 10251145 ecr 10250990], length 5

02:58:09.906247 IP localhost.6666 > localhost.44195: Flags [.], ack 46, win 342, options [nop,nop,TS val 10251145 ecr 10251145], length 0

有9组数据包的发送。(这里的Flags [P.]代表Push的含义)

此时服务器还没有close掉连接,实现了客户端保活连接的功能。然后我们停止发送ping,过了5秒后tcpdump就会输出一组:02:58:14.811761 IP localhost.6666 > localhost.44195: Flags [F.], seq 1, ack 46, win 342, options [nop,nop,TS val 10251636 ecr 10251145], length 0

02:58:14.816420 IP localhost.44195 > localhost.6666: Flags [.], ack 2, win 342, options [nop,nop,TS val 10251637 ecr 10251636], length 0

服务端那边发送了FIN包,说明服务端close掉了连接。服务端的输出如下:

~/codeDir/phpCode/hyperf-skeleton # php server.php

string(17) "Client: Connect 1"

string(5) "ping

"

string(5) "ping

"

string(5) "ping

"

string(5) "ping

"

string(5) "ping

"

string(5) "ping

"

string(5) "ping

"

string(5) "ping

"

string(5) "ping

"

string(10) "close fd 1"

然后我们在客户端那边ctrl + c来关闭连接:

~/codeDir/phpCode/hyperf-skeleton # nc 127.0.0.1 6666

ping

ping

ping

ping

ping

ping

ping

ping

ping

^Cpunt!

~/codeDir/phpCode/hyperf-skeleton #

此时,tcpdump的输出如下:

03:03:02.257667 IP localhost.44195 > localhost.6666: Flags [F.], seq 46, ack 2, win 342, options [nop,nop,TS val 10280414 ecr 10251636], length 0

03:03:02.257734 IP localhost.6666 > localhost.44195: Flags [R], seq 2678621620, win 0, length 0

应用层心跳

1、制定ping/pong协议(mysql等自带ping协议)

2、客户端灵活的发送ping心跳包

3、服务端OnRecive检查可用性回复pong

例如:

$server->on('receive', function (\Swoole\Server $server, $fd, $reactor_id, $data)

{

if ($data == 'ping')

{

checkDB();

checkServiceA();

checkRedis();

$server->send('pong');

}

});

结论

1、tcp的keepalive最简单,但是有兼容性问题,不够灵活

2、swoole提供的keepalive最实用,但是需要客户端配合,复杂度适中

3、应用层的keepalive最灵活但是最麻烦

以上就是PHP Swoole长连接常见问题总结的详细内容

更多学习内容请访问:八重樱:怎么从一名码农成为架构师的必看知识点:目录大全(不定期更新)​zhuanlan.zhihu.com

我的官方群点击此处。

swoole学习推荐视频

PHP—swoole通往大神修炼之路:av77924246

手把手教你用swoole+websocket实现户外监控直播(总集篇):av79087951

教你用swoole开发网络游戏:av79264440

PHP高级技术手写swoole分布式框架:av78383962

PHP高级技术手写swoole分布式框架(二):av78632435

PHP高级技术手写swoole分布式框架(三):av78748923

PHP高级技术手写swoole分布式框架(框架优化):av78856427

PHP高级技术手写swoole分布式框架(分布式RPC):av79012272

用swoole实现消息推送:av79874641

swoole+docker+redis主从复制及读写分离av78781841



推荐阅读
  • PHP语言之所以能有今天的地位,得益于PHP语言设计者一直遵从实用主义,将技术的复杂性隐藏在底层。PHP语言入门简单,容易掌握,程序健壮性好。 ... [详细]
  • redis 获取不到_redis 缓存锁的实现方法
    1.redis加锁分类redis能用的的加锁命令分表是INCR、SETNX、SET2.第一种锁命令INCR这种加锁的思路是,key不存在,那么key的值 ... [详细]
  • 电商系统设计艺术——秒杀业务设计
    一、秒杀场景人多货少,只有少量的人能够抢购成功。高并发,秒杀业务在开始之前流量比较平稳,开始后流量会直线性的上升。持续时间短࿰ ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 基于分布式锁的防止重复请求解决方案
    一、前言关于重复请求,指的是我们服务端接收到很短的时间内的多个相同内容的重复请求。而这样的重复请求如果是幂等的(每次请求的结果都相同,如查 ... [详细]
  • 1.判断磁盘使用空间df-h|grep-vFile|seds%g|awk{if($580)print使用空间超过了80%:,$6}或#!binbash#Filename:di ... [详细]
  • 导读:今天编程笔记来给各位分享关于php织梦跟帝国是什么的相关内容,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!本文目录一览: ... [详细]
  • 【宇润日常疯测007】Swoole 协程与传统 fpm 同步模式比较
    为什么80%的码农都做不了架构师?如果说数组是PHP的精髓,数组玩得不6的,根本不能算是会用PHP。那协程对于Swoole也是同理& ... [详细]
  • PHP socket服务端与客户端的简易通信
    今天学习socket通信的同时,顺便整理了下以前初识socket的知识。现在关于php的socket通信,有些框架已经十分成熟了,比如swoole和workerman,这两个大家可以学习学 ... [详细]
  • Swoole在PHP-fpmapache中如何使用task功能?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人 ... [详细]
  • 本文探讨了容器技术在安全方面面临的挑战,并提出了相应的解决方案。多租户保护、用户访问控制、中毒的镜像、验证和加密、容器守护以及容器监控都是容器技术中需要关注的安全问题。通过在虚拟机中运行容器、限制特权升级、使用受信任的镜像库、进行验证和加密、限制容器守护进程的访问以及监控容器栈,可以提高容器技术的安全性。未来,随着容器技术的发展,还需解决诸如硬件支持、软件定义基础设施集成等挑战。 ... [详细]
  • WindowsXP常见的进程列表最基本的系统进程(也就是说,这些进程是系统运行的基本条件,有了这些进程,系统就能正常运行&# ... [详细]
  • 哪里有php培训(php培训哪家好)
    导读:今天编程笔记来给各位分享关于哪里有php培训的相关内容,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!本文目录一览: ... [详细]
  • |Q|A|-------------------|-----|Bugreport?|ye ... [详细]
  • php在哪里好找工作(php学到什么程度可以找到工作)
    导读:本篇文章编程笔记来给大家介绍有关php在哪里好找工作的相关内容,希望对大家有所帮助,一起来看看吧。本文目录一览:1、php去哪个城 ... [详细]
author-avatar
大眼睁睁
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有