杰克工作室 发表于 2023-2-23 16:20

高并发下PHP请求Redis异常处理

<p>最近发现线上服务器经常报连接redis异常:Uncaught exception &#39;RedisException&#39; with message &#39;Redis server went away&#39;。&nbsp;<br />
于是摘下一台线上机,对服务器一半以上的接口进行压测:</p>
$ http_load -p 100 -f 100000 urls.txt<br />
100000 fetches, 100 max parallel, 2.46403e+09 bytes, in 46.6896 seconds<br />
24640.3 mean bytes/connection<br />
2141.81 fetches/sec, 5.27747e+07 bytes/sec<br />
msecs/connect: 1.04977 mean, 3.755 max, 0.859 min<br />
msecs/first-response: 44.0343 mean, 142.5 max, 11.804 min<br />
HTTP response codes:<br />
&nbsp; code 200 -- 100000<br />
$ http_load -p 100 -s 100 urls.txt<br />
145919 fetches, 100 max parallel, 3.65654e+09 bytes, in 100 seconds<br />
25058.7 mean bytes/connection<br />
1459.19 fetches/sec, 3.65654e+07 bytes/sec<br />
msecs/connect: 1.10872 mean, 14.339 max, 0.863 min<br />
msecs/first-response: 65.4696 mean, 1012.95 max, 12.124 min<br />
11082 bad byte counts<br />
HTTP response codes:<br />
&nbsp; code 200 -- 134837<br />
&nbsp; code 500 -- 11082
<p>发现当接口的请求量达到10w的时候,QPS可以达到2000以上;请求量超过10w的时候,QPS会降到1500以下,需要访问redis服务的接口会出现Redis server went away异常,并返回500。</p>

<p><strong>难道说现在机器负载的瓶颈就是redis了吗?压力测试时的redis服务器压力大么?</strong></p>

<p><img alt="" src="data/attachment/forum/202302/23/161432eici2juw2ppxljpz.jpg" style="height:574px; width:1326px" /><br />
<br />
<img alt="" src="data/attachment/forum/202302/23/161534n3k3xka9x4a3xa9a.jpg" style="height:588px; width:1345px" /></p>

<p>从监控的趋势图可以看到,压测接口请求的redis最高QPS也就20000,客户端连接数只有不到80,压力并不大。所以瓶颈不会是在redis服务器上。</p>

<p>在排除了Linux,php,nginx,redis配置方面可能出现的问题后,<strong>最后锁定问题,应该是在连接Redis的时候出现异常。</strong>那PHP是如何连接redis的呢?使用redis扩展。而问题很有可能出在php的redis扩展上,就是phpredis。</p>

<p>通常我们在使用phpredis连接redis的时候,都是用的<strong>connect()</strong>方法,在大量的php请求请求到redis的时候,这种方式会不断的建立和关闭连接,占用很多资源,导致服务器出现大量的TIME_WAIT,这样就会有很多php请求无法连接redis,出现Redis server went away这个异常,实际就说明redis服务没有收到请求,所以昨天看到在压测过程中redis负载压力并不大,但php还是返回500。&nbsp;<br />
之前的思路都是以为redis层面问题或者服务器配置甚至硬件问题,在换个思路之后,发现phpredis还有个连接redis的方法,就是<strong>pconnect()</strong>,这个方法很多人都知道,但是很少把它和高并发联系到一起,pconnect()和connect()的区别网上有很多,总结一下就是pconnect方法建立后的连接并不随这请求的结束而关闭,而是依赖于php-fpm进程,php-fpm进程不死,redis connect就一直存在,直到空闲超时自动断开(目前redis timeout 600),这样就极大的提高了redis connect的重复利用,减少大量的IO开销。不过这样也就出现一个瑕疵,就是redis的connect clients数量要比以前高出很多,目前线上机单台服务器php-fpm进程300,线上服务器共有12台,单台redis最高connect clients可以达到3600,不过这也远低于单台redis配置的maxclients 200000。</p>

<p>所以我在之前的机器上做了修改,然后重新压测:</p>

<pre>
$ http_load -p 100 -s 100 urls.txt
253884 fetches, 100 max parallel, 6.44571e+09 bytes, in 100 seconds
25388.4 mean bytes/connection
2538.84 fetches/sec, 6.4457e+07 bytes/sec
msecs/connect: 1.07964 mean, 13.918 max, 0.858 min
msecs/first-response: 36.6679 mean, 250.973 max, 7.66 min
1 bad byte counts
HTTP response codes:
code 200 -- 253884</pre>

<p>可以看到100个进程跑了100s,请求量253884,QPS达到2538.84,这个数字基本正常。&nbsp;<br />
再看一下redis负载情况:&nbsp;<br />
<br />
<img alt="" src="data/attachment/forum/202302/23/161628jc3gxgxdohi0wxwz.jpg" style="height:567px; width:1339px" /><br />
<br />
<img alt="" src="data/attachment/forum/202302/23/161808pbsvtjghjbzfgrvh.jpg" style="height:483px; width:1340px" /><br />
重新上线了三台机器之后,可以看到redis端connect_clients明显增加,但是QPS还是很平稳(请忽略压测导致的凸起点)。</p>

<p>最后有必要反思一下这个过程,其实最后解决问题的方法是有点狗血的,<strong>因为pconnect方法很多人都知道,但是都没有把这个方法和高并发请求redis联系起来</strong>,所以我严重怀疑很多人对这个方法理解的并不深刻。在遇到问题时,一定要敢于尝试,如果你发现这个问题Google上都没有答案的时候,你应该觉得这是个挑战,所以不要轻易放弃。还有眼界要广,不要纠结于一个点,像redis这么成熟的软件系统,十万级的访问量基本不会构成威胁,所以要检查一个请求经过的每一个环节。</p>

<p>&nbsp;</p>

<p>原文地址:http://blog.csdn.net/u013474436/article/details/53117463</p>
页: [1]
查看完整版本: 高并发下PHP请求Redis异常处理