Symfony 2阻止并发 [英] Symfony 2 blocked concurrency

查看:74
本文介绍了Symfony 2阻止并发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Symfony 2.5应用程序,并且在请求并发方面有一些奇怪的问题.

I have a Symfony 2.5 application and I have some weird problems with request concurrency.

为演示此问题,我创建了两个路由,分别称为/time/sleep.控制器的主体非常简单:

To demonstrate the issue I've created two routes called /time and /sleep. The controller's bodies are quite simple:

timeAction():
    time();

sleepAction()
    sleep(30);

当我在浏览器中请求/time路由时,它将立即以当前时间戳作为响应.但是,当我第一次请求/sleep路由,然后是/time路由时,它只是挂在这里,直到sleep()完成.只有在此之后,/time控制器才会响应时间戳.换句话说-一个请求阻止了所有其他请求.起初我什至没有注意到这一点,但是当您有长期执行计划的请求时,这很明显.

When I request the /time route in my browser - it responds momentarily with the current timestamp. However, when I first request the /sleep route and then the /time route - it just hangs in there until sleep() is completed. Only after that the /time controller will respond with the timestamp. In other words - one request is blocking all the others. I didn't even noticed this at first, but when you have requests with the long execution plan - it becomes apparent.

这可能是什么原因?

我仍然要自己做一些附加测试,以深入了解这种情况.我将尝试用更多详细信息更新问题.

I'm still going to do some additional tests on my own to dig deeper in the situation. I will try to update the question with more details.

推荐答案

更新

看起来像PdoSessionHandler现在使用了自己的某种锁定机制,该机制可以防止并发请求.旧的解决方案将不再可用.

Update

Looks like PdoSessionHandler is now uses some locking mechanism of it's own that will prevent concurrent requests. The old solution will no longer work out of the box.

并发问题的官方解决方案是在请求处理周期中尽快关闭会话.您可以通过调用$session->close() session_write_close() 来实现.

The official solution to the concurrency problem is to close the session as soon as possible in the request handling cycle. You can do this by calling $session->close() or session_write_close().

但是,如果您确定应用程序中不会出现会话数据冲突,则可以安全地禁用PDO会话处理程序配置中的锁定:

However, if you are sure that session data conflicts will not arise in your application you can safely disable locking in the configuration of the PDO session handler:

# services.yml

session.handler.pdo:
        class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
        public: false
        arguments:
            - "pgsql:host=%database_host%;port=%database_port%;dbname=%database_name%"
            - db_username: %database_user%
              db_password: %database_password%
              db_table: session
              db_id_col: session_id
              db_data_col: session_value
              db_time_col: session_time
              db_lifetime_col: session_lifetime
              lock_mode: 0 # LOCK_NONE

您可以在本期中阅读更多信息: https://github.com/symfony/symfony/pull/10908

You can read more in this issue: https://github.com/symfony/symfony/pull/10908

感谢 Crozin 为我指出了有助于解决问题的正确方向.我将在此处添加其他信息,希望将来可以帮助某人节省一些时间.

Thanks to Crozin who pointed me in the right direction that helped to solve my problem. I will put additional information here that I hope will help someone in the future to save some time.

在以下主题中也描述了该问题:

The issue is also described in the following topics:

  • How do I configure Apache2 to allow multiple simultaneous connections from same IP address?
  • Simultaneous Requests to PHP Script

问题在于,默认情况下,PHP使用的是基于文件的会话处理.换句话说,会话数据存储在服务器文件系统中的特定文件中.并且为了保护该文件免于意外同时写入,使用了文件锁定机制.这是计算机科学中的经典锁定问题.对PHP的第一个请求将在会话文件上获得一个锁,而所有其他请求将必须等待此锁被释放.而且,如果您在多请求环境中有持久的请求(例如同时进行AJAX请求或页面上有多个框架),它将变得显而易见.

The problem is that PHP by default is using file-based session handling. In other words, session data is stored in the specific file in the server's filesystem. And in order to protect this file from accidental simultaneous writing, file locking mechanism is used. This is a classic locking problem in computer science. The first request to the PHP will gain a lock on the session file and all other requests will have to wait for this lock to be released. And if you have a long-lasting requests in multi-request environment (like with simultaneous AJAX requests or multiple frames on a page) it will become apparent.

可以通过在脚本完成之前提早调用 session_write_close() 来解决问题,但毕竟会话操作可以完成,也可以切换到其他会话存储机制,例如数据库会话存储.

The problem can be solved by either calling session_write_close() prematurelly, before script is finished, but after all session manipulations are done or by switching to another session storage mechanism, like database session storage.

我认为,在 Symfony 2 中,最好的做法是将与PDO处理程序的会话存储在您选择的数据库中.这是如何设置的官方教程:

I think, that in Symfony 2 the best course of action is to store session with the PDO handler (in a database of your choice). Here's the official tutorial of how it can be set up:

如何使用PdoSessionHandler在数据库中存储会话.

提示:如果使用的是Doctrine迁移,则可以创建一个新的迁移类,并向其中添加创建会话存储表所需的SQL.

HINT: If you are using Doctrine migrations, then you can create a new migration class and add SQL required to create table for session storage to it.

使用这种方法,您将拥有更好的非阻塞会话存储机制,并且您的应用程序将能够水平扩展.

With this approach you will have a better non-blocking session storage mechanism and your application will be able to scale horizontally.

这篇关于Symfony 2阻止并发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆