该问题是由PHP会话处理程序放置的锁引起的。因此,这不是Magento明确锁定某些内容并试图阻止管理员请求,而是基于文件的会话存储本身的几乎副作用。
当通过初始(长时间运行)请求打开会话数据文件时,会将写锁放置在会话数据文件上,从而导致第二个请求阻塞,直到调用它时释放该锁为止session_start
。Mage_Core_Model_Session_Abstract_Varien::start
这是100%可复制的。我使用了与您相同的方法,sleep(30)
在Mage_Adminhtml_IndexController::globalSearchAction
值得注意的是,如果您正在使用数据库会话存储,则无法复制。找到根本原因后,我将一个沙箱设置为数据库会话存储,不再能够重现该问题。因此,Magento的数据库会话处理程序似乎不使用行级锁定来锁定会话写入。我发现这很有趣,因为它有可能丢失会话数据,因为该应用程序显然没有考虑写入同一会话的多个线程。读者注意事项:我绝不会在生产中使用db会话存储来尝试解决此问题,这仅适用于重载MySql数据库。
我没有尝试使用基于内存的会话存储系统(例如Redis)来重现该行为,但我的猜测是在这些存储中也可能忽略了锁定会话存储中的记录。
有一些技术可以避免这种情况,例如session_write_close
在开始长期运行的作业之前先使用释放锁。但是,这也将阻止您写入该会话,因为您刚刚将其关闭。因此,它不太可能在Magento中全面实现,而有可能在特定的路径/控制器上实现。
我将其固定为根本原因的技术是启用Xdebug分析器并检查“ cachegrind”文件。第二个请求完成后,我将输出文件(约25 MB日志)加载到MacCallGrind中,并沿着包含最终时间为28秒或更长的调用路径深入到跟踪中。最终,我session_start
花了大约28秒才能完成通话,这给了我很好的研究依据。
编辑:对于感兴趣的人,我已经在Twitter的MacCallGrind中发布了 “ cachegrind”文件的屏幕截图。