Symfony 2阻止了并发性(Symfony 2 blocked concurrency)

编程入门 行业动态 更新时间:2024-10-27 17:20:04
Symfony 2阻止了并发性(Symfony 2 blocked concurrency)

我有一个Symfony 2.5应用程序,我有一些奇怪的请求并发问题。

为了演示这个问题,我创建了两个名为/time和/sleep路由。 控制器的主体非常简单:

timeAction(): time(); sleepAction() sleep(30);

当我在浏览器中请求/time路由时,它会使用当前时间戳立即响应。 但是,当我第一次请求/sleep路由然后是/time路由时 - 它只是挂在那里直到sleep()完成。 只有在那之后/time控制器才会响应时间戳。 换句话说 - 一个请求阻止了所有其他请求。 起初我没有注意到这一点,但是当你有很长时间的执行计划的要求时 - 就会变得很明显。

这可能是什么原因?

我还是要自己做一些额外的测试来深入挖掘这种情况。 我将尝试更详细地更新问题。

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

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);

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.

What could be the reason for this?

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现在使用它自己的一些锁定机制来阻止并发请求。 旧的解决方案将不再适用。

并发问题的官方解决方案是在请求处理周期中尽快关闭会话。 你可以通过调用$session->close()或session_write_close()来做到这一点。

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

# 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

老解决方案

感谢Crozin指出我正确的方向,这有助于解决我的问题。 我会在这里提供更多信息,希望能够帮助未来的人节省一些时间。

以下主题还介绍了此问题:

如何配置Apache2以允许来自同一IP地址的多个同时连接? 同时向PHP脚本请求

问题在于,PHP默认使用基于文件的会话处理。 换句话说,会话数据存储在服务器文件系统的特定文件中。 并且为了保护该文件免于意外同时写入,使用文件锁定机制。 这是计算机科学中的经典锁定问题 。 向PHP发出的第一个请求将获得对会话文件的锁定,并且所有其他请求将不得不等待此锁释放。 如果您在多请求环境中有长时间的请求(例如在页面上同时发生AJAX请求或多帧),它将变得明显。

这个问题可以通过在脚本完成之前预先调用session_write_close() ,但是在完成所有会话操作之后调用session_write_close()或者通过切换到另一个会话存储机制(如数据库会话存储)来解决。

我认为,在Symfony 2中 ,最好的行动方式是将会话存储在PDO处理程序中(在您选择的数据库中)。 以下是如何设置它的官方教程:

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

提示:如果您正在使用Doctrine迁移,那么您可以创建一个新的迁移类并添加创建用于会话存储的表所需的SQL。

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

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.

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().

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
 

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

Old solution

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

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.

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.

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:

How to Use PdoSessionHandler to Store Sessions in the Database.

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.

更多推荐

本文发布于:2023-07-22 07:01:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1217931.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:发性   Symfony   concurrency   blocked

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!