使用Memcached进行会话时打开的文件过多

Jav*_* H. 5 php memcached nginx symfony

我正在使用ubuntu + nginx + php5-fpm + symfony2,最近我从文件切换到Memcached(php5-memcached)进行会话。从那以后,一段时间后,我开始收到500个响应代码错误。这是错误日志中的样子(打开流失败:打开太多):

"PHP message: PHP Warning:  simplexml_load_file(.../vendor/friendsofsymfony/user-bundle/FOS/UserBundle/Resources/config/doctrine/User.orm.xml): failed to open stream: Too many open files in .../vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php on line 736
PHP message: PHP Warning:  simplexml_load_file(): I/O warning : failed to load external entity ".../vendor/friendsofsymfony/user-bundle/FOS/UserBundle/Resources/config/doctrine/User.orm.xml" in .../vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php on line 736
PHP message: PHP Warning:  include(.../vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/MappingException.php): failed to open stream: Too many open files in .../vendor/composer/ClassLoader.php on line 382
Run Code Online (Sandbox Code Playgroud)

经过一些研究,我发现它似乎与Memcached在会话中的使用有关:

  1. 这些是正在运行的php5-fpm进程:

    ps aux | grep php

    根3233 0.0 0.2 207464 16164 Ss Mar16 0:02 php-fpm:主进程(/etc/php5/fpm/php-fpm.conf)www-data
    3236 0.0 0.8 238796 65796?S Mar16 0:50 php-fpm:池www
    www-data 3646 0.0 0.7 231176 57808?S Mar16 0:42 php-fpm:池www
    www-data 7503 0.1 0.7 234820 59920?S 11:52 0:34 php-fpm:池www
    ubuntu 8224 0.0 0.0 10436 860 pts / 0 S + 16:53 0:00 grep --color = auto php

2,使用lsof检查任何php5-fpm子级,除其他外,它显示有1k +个打开的文件描述符指向具有已建立连接的Memcache实例:

sudo lsof -p 3236

…
php5-fpm 3236 www-data  672u  IPv4             107781      0t0    TCP hostname:54403->memcacheIp:11211 (ESTABLISHED)
php5-fpm 3236 www-data  673u  IPv4             108827      0t0    TCP hostname:54411->memcacheIp:11211 (ESTABLISHED)
php5-fpm 3236 www-data  674u  IPv4             107800      0t0    TCP hostname:54418->memcacheIp:11211 (ESTABLISHED)
... 
Run Code Online (Sandbox Code Playgroud)

超过一千条这样的条目,我认为它到达1024时就会开始失败,这是打开文件描述符的限制:

ulimit  -n
1024
Run Code Online (Sandbox Code Playgroud)

事实是,这是一个不用于1个或2个并发用户的QA环境,因此,所有这些打开文件描述符都不是活动会话。我注意到,每次我向symfony2应用程序发出请求时,都会打开与内存缓存服务器的新套接字连接,但是从不关闭它。因此,最终它在开始失败时达到极限。我以为可能与超时连接有关,或类似的东西,但到目前为止还没有找到任何东西。

重新启动php5-fpm似乎可以“解决”该问题,但是仅在发出另一个1k请求之前。

我暂时切换回文件,问题消失了。我也尝试使用Memcachephp5-memcache)代替,Memcached问题得到解决,但我宁愿使用php5-memcached它,因为它似乎可以更好地维护。

关于如何解决此问题的任何想法?

非常感谢你!

如果有帮助,请使用我使用的特定php版本:

PHP 5.5.9-1ubuntu4.6 (cli) (built: Feb 13 2015 19:17:11) 
libmemcached version => 1.0.8
Run Code Online (Sandbox Code Playgroud)

Jav*_* H. 4

好吧,我已经找到了解决方案,感谢这个:

http://php.net/manual/en/memcached.construct.php(参见@Tobias评论)

和这个https://gist.github.com/K-Phoen/4327229(参见@cmenning 评论)

我将 Memcached 与 session.yml 一起使用,如下所示:

session.memcached:
    class: Memcached
    arguments:
       persistent_id: %session_memcached_prefix%
    calls:
        - [ addServer, [ %session_memcached_host%, %session_memcached_port% ]]

session.handler.memcached:
    class:     Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler
    arguments: [@session.memcached, { prefix: %session_memcached_prefix%, expiretime: %session_memcached_expire% }]
Run Code Online (Sandbox Code Playgroud)

事实证明,如果您为 Memcached 提供 permanent_id 参数,它将保留请求之间的连接。来自 php.net:

持久ID

默认情况下,Memcached 实例在请求结束时被销毁。要创建在请求之间保留的实例,请使用perpeter_id 为该实例指定唯一ID。使用相同的 persist_id 创建的所有实例将共享相同的连接。

问题是,如果您使用 permanent_id (这对性能更好),您必须在调用之前检查是否已经添加了服务器addServer,否则它将添加一个新服务器并创建一个新连接,即使它是同一台服务器(它不会检查是否存在欺骗行为。类似这样的事情:

        $instance = new Memcached($persistent_id);

        // Add server if no connections listed. 
        if (!count($instance->getServerList())) {
            $instance->addServers($server);
        }
Run Code Online (Sandbox Code Playgroud)

但为此,您需要创建 Memcached 的包装类。简单的解决方法就是注释掉这一行,并暂时避免使用持久连接:

    #arguments:
    #  persistent_id: %session_memcached_prefix%
Run Code Online (Sandbox Code Playgroud)

这解决了问题,尽管并不理想。我希望它能帮助您节省一些时间并避免头痛!感谢大家的帮助。