我使用的是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
root 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?S Mar16 0:50 php fpm:池www
www数据3646 0.0 0.7 231176 57808?S Mar16 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分/0 S+16:53 0:00 grep--颜色=自动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)
...
超过一千个这样的条目,我认为当它达到1024时就会开始失败,这是打开文件描述符的限制:
ulimit -n
1024
问题是,这是一个QA环境,不适用于超过1或2个并发用户,因此不可能所有打开的文件描述符都是活动会话。我注意到,每次我向symfony2应用程序发出请求时,都会打开一个与memcache服务器的新套接字连接,但它从未关闭。因此,最终它在开始失败时达到了极限。我想可能是与超时连接或类似的东西有关,但到目前为止还没有发现任何东西。
重新启动php5-fpm似乎可以"解决"这个问题,但只有在发出另外1k个请求之后。
我暂时切换回文件,问题就消失了。此外,我尝试使用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
好吧,我找到了解决方案,这要归功于:
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%
这解决了问题,尽管这并不理想。我希望它能帮你节省一些时间,避免头疼!感谢大家的帮助。
我们遇到了这个问题,我们通过在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