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

时间:2015-03-18 09:35:26

标签: symfony nginx memcached php

我正在使用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

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

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

    ps aux | grep php

    root 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:pool www
    www-data 3646 0.0 0.7 231176 57808? S Mar16 0:42 php-fpm:pool www
    www-data 7503 0.1 0.7 234820 59920? S 11:52 0:34 php-fpm:pool www
    ubuntu 8224 0.0 0.0 10436 860 pts / 0 S + 16:53 0:00 grep --color = auto php

  2. 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)
    ... 
    

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

    ulimit  -n
    1024
    

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

    重新启动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
    

2 个答案:

答案 0 :(得分:4)

好的,我已经找到了解决方案:

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

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

我正在使用Memcached和sessions.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% }]

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

persistent_id

  

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

问题在于,如果您使用persistent_id(性能更好),则必须在调用addServer之前检查是否已添加服务器,否则将添加新服务器&创建一个新的连接,即使它是同一个服务器(它没有检查欺骗。 像这样:

        $instance = new Memcached($persistent_id);

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

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

    #arguments:
    #  persistent_id: %session_memcached_prefix%

这解决了这个问题,即使它并不理想。 我希望它可以帮助您节省一些时间,避免头痛! 谢谢大家的帮助。

答案 1 :(得分:0)

我们遇到了这个问题,我们通过在config.yml

中的memcache中缓存元数据来解决这个问题
 orm:

    default_entity_manager:   default
    entity_managers:
      default:
         metadata_cache_driver:
             type: memcache
             host: localhost
             port: 11211
             instance_class: Memcache
         query_cache_driver:
             type: memcache
             host: localhost
             port: 11211
             instance_class: Memcache

否则,如果你喜欢通过apc缓存它也修复它,我试过两个:

 metadata_cache_driver: apc
 query_cache_driver: apc

希望这有助于了解有关此问题的更多信息:https://github.com/FriendsOfSymfony/FOSUserBundle/issues/1062#issuecomment-29473883