Слишком много открытых файлов при использовании 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 для сеансов:
-
Это процессы 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
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%
Это решает проблему, хотя и не идеально. Я надеюсь, что это поможет вам сэкономить немного времени и избежать головной боли! Спасибо всем за ваше помощь.
У нас была эта проблема, которую мы исправили, кэшировав метаданные в 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