Слишком много открытых файлов при использовании Memcached для сеансов


Я использую 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

    Корень 3233 0,0 0,2 207464 16164? Ss Mar16 0:02 php-fpm: главный процесс (/etc/php5/fpm/php-fpm.conf)
    www-данные 3236 0.0 0.8 238796 65796? С 16 марта 0:50 php-fpm: пул www
    www-данные 3646 0.0 0.7 231176 57808? С 16 марта 0:42 php-fpm: пул www
    www-данные 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 С+ 16:53 0:00 grep--цвет=авто php

2. Проверка любого из дочерних файлов php5-fpm с помощью lsof показывает, среди прочего, что было 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

Дело в том, что это среда контроля качества, которая используется не более 1 или 2 одновременных пользователя, поэтому все эти открытые файловые дескрипторы не могут быть активными сеансами. Я заметил, что каждый раз, когда я делаю запрос в приложение symfony2, открывается новое соединение сокета с сервером memcache, но затем оно никогда не закрывается. Таким образом, в конце концов он достигает предела при начале сбоя. Я думал, что может быть что-то связанное с тайм-аутом соединений или что-то в этом роде, но пока ничего не нашел.

Перезапуск php5-fpm, похоже, "решает" проблему, но только пока не будет сделано еще 1 тыс. запросов.

Я временно переключился обратно на файлы, и проблема исчезла. Также я пробовал использовать Memcache (php5-memcache) вместо Memcached и проблема решена, но я бы предпочел использовать php5-memcached, поскольку он кажется лучше поддерживаемым.

Есть идеи, как решить эту проблему?

Большое вам спасибо!

Если это поможет, конкретная версия php, которую я использую:

PHP 5.5.9-1ubuntu4.6 (cli) (built: Feb 13 2015 19:17:11) 
libmemcached version => 1.0.8
Author: Javier C. H., 2015-03-18

2 answers

Хорошо, я нашел решение благодаря этому:

Http://php.net/manual/en/memcached.construct.php (см. комментарий @Tobias)

И это https://gist.github.com/K-Phoen/4327229 (см. комментарий @cmenning)

Я использовал Memcached с сеансами.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% }]

Оказывается, что если вы предоставите аргумент persistent_id для Memcached, он сохранит соединение между запросами. От php.net :

Идентификатор Сохранения_id

По умолчанию экземпляры Memcached уничтожаются в конце запроса. Чтобы создать экземпляр, который сохраняется между запросами, используйте persistent_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%

Это решает проблему, хотя и не идеально. Я надеюсь, что это поможет вам сэкономить немного времени и избежать головной боли! Спасибо всем за ваше помощь.

 4
Author: Javier C. H., 2015-03-18 13:06:28

У нас была эта проблема, которую мы исправили, кэшировав метаданные в memcache в вашей конфигурации.yml

 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

 0
Author: Nawfal Serrar, 2015-03-18 09:41:45