利用PHP长连接提高性能

更新时间:

问题介绍

最近有 PHP 用户反馈对云数据库 Memcache 版做性能测试的结果,达不到预期的性能指标。通过了解具体情况,大多数用户在使用 PHP 连接云数据库 Memcache 版时,都是通过走 Apache WEB 服务再连云数据库 Memcache 版,使用的是短连接。而每个短连接的开销不止是 socket 重连,还有复杂的重新鉴权流程,开销比一个普通请求大许多,因此对网站的效率是有很大影响的。

解决方案

于是我们建议用户改短连接为长连接,但是云数据库Memcache要求使用的PHP MEMCACHED扩展,不像memcache扩展那样有个pconnect接口。如何才能在PHP中建立长连接,以下教程供大家参考。

PHP 官网介绍 memcached 构造函数时有下面一段话:

  1. 说明
  2. Memcached::__construct ([ string $persistent_id ] )创建一个代表到Memcached服务端连接的Memcached实例。
  3. 参数
  4. persistent_id默认情况下,Memcached实例在请求结束后会被销毁。但可以在创建时通过persistent_id为每个实例指定唯一的ID, 在请求间共享实例。所有通过相同的persistent_id值创建的实例共享同一个连接。

即在调用构造函数时传给它一个同样的 persistent_id 就能实现共享连接。代码实现如下:

  1. <?php
  2. $memc = new Memcached(‘ocs’);//这里的ocs,就是persistent_id
  3. if (count($memc->getServerList()) == 0) /*建立连接前,先判断*/
  4. {
  5. echo "New connection"."<br>";
  6. /*所有option都要放在判断里面,因为有的option会导致重连,让长连接变短连接!*/
  7. $memc->setOption(Memcached::OPT_COMPRESSION, false);
  8. $memc->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
  9. $memc->setOption(Memcached::OPT_TCP_NODELAY, true); //重要,php memcached有个bug,当get的值不存在,有固定40ms延迟,开启这个参数,可以避免这个bug
  10. /* addServer 代码必须在判断里面,否则相当于重复建立’ocs’这个连接池,可能会导致客户端php程序异常*/
  11. $memc->addServer("your_ip", 11212);
  12. $memc->setSaslAuthData("user", "password");
  13. }
  14. else
  15. {
  16. echo "Now connections is:".count($memc->getServerList())."<br>";
  17. }
  18. $memc->set("key", "value");
  19. echo "Get from OCS: ".$memc->get("key");
  20. //$memc->quit();/*代码结束的地方一定不能加quit,否则变短连接!*/
  21. ?>

上述代码要特别注意的三个地方都加了注释。构造函数里的“ocs”关键字,就相当于一个连接池了,需要使用的连接调用 new Memcached(‘ocs’) 就能从池里获取连接。

执行结果

将上述代码放到 Apache 工作路径/var/www/html/下面,命名为 test.php。然后再浏览器输入: http://your_ip:80/test.php 。前8次浏览器都输出结果为:

  1. New connection
  2. Get from OCS: value

即这8次都是新建连接,而8次后都是复用这8个链接了。抓包结果也显示8次后都是长连接,无 socket 重连无鉴权。

那么为什么有8个链接?通过查看 httpd.conf 配置文件,是因为 appache 启动了8个子进程:

  1. #StartServers:numbers of server processes to start
  2. StartServers 8

于是刚好在‘ocs’这个 persistent_id 的连接池里面初始化8个连接,此后的请求就用这8个链接了。

接下来测试页面跳转,拷贝一个 php 文件,建立连接的构造函数的 persistent_id 还是用 “ocs”。得到的结果是从一个连接换到了另一个连接上(因为调用的 Apache 子进程不一样),但无鉴权无 socket 重连过程。即 PHP memcached 的长连接设置是有效的。通常我们使用的都是 PHP-FPM 模式, FPM 进程会和 memcached server 保持长连接,因此该连接的生命周期同 Apache 进程。