【问题标题】:PHP requests one by one or simultaneouslyPHP 请求一个一个或同时
【发布时间】:2019-07-09 13:51:48
【问题描述】:

我有一些关于 PHP 以及请求如何在后台工作的问题。

1) 假设我编写了我的 PHP 应用程序并将其上传到服务器。现在我编写了一个函数,如果用户转到执行该函数的路径,就会发生一些事情。

问题是:如果一个用户发出请求,另一个用户也发出请求,那么第二个用户是否必须等到第一个用户的请求完成? (通过说请求完成,我的意思是直到我写的函数被执行到最后)。这是正确的猜测还是执行哪个函数都没有关系。在请求未完成之前,第二个请求永远不会开始?

2) 我有我的 PHP 应用程序。想象一下两个人同时发出请求,将数据写入数据库(不是写入,而是更新)。假设我使用了负载均衡器。如果一个用户向 balancer1 发出请求,而另一个用户向 balancer2 发出请求,我想要做的是,如果第一个用户的调用更新了数据库,第二个用户的请求必须立即停止(它不应该被更新)。

场景是我的数据库中有 jwt 令牌,用于在第三方工具上执行请求。它的有效期为 1 小时。假设1小时过去了。如果一个用户调用更新令牌并且第二个用户也调用更新令牌,将会发生的情况是第二个用户将更新令牌而第一个用户的令牌将无效,这是不好的。

【问题讨论】:

    标签: php http load-balancing


    【解决方案1】:

    PHP 将同时处理请求。处理一个请求不会为不同用户锁定另一个请求。但是,如果 PHP 会话被第一个请求锁定,PHP 可以一个一个地执行来自同一个用户的请求。第二个请求将在会话关闭时进行。

    尝试在第一个浏览器选项卡上使用sleep(30) 运行 PHP 脚本:

    <?
    session_start();
    
    sleep(30);
    

    另一个浏览器选项卡上的另一个脚本:

    <?
    session_start();
    
    echo 'hello';
    

    脚本 #2 在第一个完成之前不会执行。

    这很重要,因为您在每个应用程序中都使用会话。

    【讨论】:

    • 第二个问题,我不确定。
    • @NikaKhurashvili 我添加了一个可以用来验证它的测试。
    【解决方案2】:
    1. 如果您有一个由控制器功能提供服务的路由,则对于每个请求,都会有一个单独的控制器实例化。例如:用户 A 和用户 B 请求相同的路由laravel.com/stackoverflow,控制器准备好响应每个请求,与同时请求的用户数量无关。您可以将类似视为任何服务的流程原则。例如,Laravel 在 PHP 上运行。因此,每次我们需要 PHP 处理任何脚本时,PHP 都会创建进程线程。同样,Laravel 为每个请求实例化控制器。
    2. 对于同一用户发送多个请求,仍然会像第 1 点一样处理。
    3. 如果您想一一处理特定请求,您可以queue 作业。例如,假设您要处理付款。您有 5 个请求发生。因此,控制器将同时接收所有请求,但控制器功能可以调度排队的作业,并且这些作业会被一一处理。
    4. 考虑到两个人尝试请求具有数据库更新功能的同一条路线,您可以阅读一篇关于乐观和悲观锁定的好文章here

    【讨论】:

    • 谢谢。下面的@nikolai 也回答了这个问题,他说:“但是 PHP 会一个一个地执行来自同一个用户的请求。它会发生,因为 PHP 会锁定用户的会话文件,直到请求完成。并且不会执行另一个请求除非会话被解锁。” .你对此有什么看法?你说的相反。
    • 我不相信 laravel 甚至核心 php 有任何机制来锁定用户的同时请求。简单的测试方法是获取一个 laravel url 或一个可在 web 上打开的简单 php 脚本,并同时在多个选项卡中打开它。
    • 一般@MihirBhende 是正确的,PHP 将同时执行来自同一用户的请求。但是 PHP 确实有一种机制来锁定请求,如果会话打开它就会发生。检查文档us.php.net/manual/en/function.session-write-close.php。我会更新我的答案。
    【解决方案3】:

    我应该投票结束这个 - 它太宽泛了......但我会试一试。

    如果请求依赖于一次只能执行一项任务的资源,则它们不能同时“运行”。很有可能您可能只有一个 CPU 内核或单个磁盘 - 但是在 HTTP 请求级别(在没有应用互斥锁的代码的情况下)它们似乎同时运行 - 这就是多-任务就是一切。执行线程通常会延迟等待其他事情发生,此时操作系统任务调度程序将检查是否有任何其他任务等待运行。您可以自己轻松地进行测试:

     <?php
    
     $started=time();
     sleep(20);
     print "Ran for " . (time() - $started) " seconds";
    

    (尝试大约在同一时间在不同的浏览器窗口中访问它 - 或在同一窗口的 2 个 iframe 中)

    比较一下:

     <?php
    
     $started=time();
     $fh=fopen("/tmp/concurency_test", "w");
     flock($fh, LOCK_EX);
     sleep(20);
     flock($fh, LOCK_UN);
     print "Ran for " . (time() - $started) " seconds";
    

    这也说明了为什么不应该使用平面文件在服务器上存储数据的原因之一。请注意,PHP 中的默认会话处理程序在脚本打开会话数据期间使用基于文件的锁定。

    数据库采用多种策略来避免恢复为单一操作队列 - 最常见的是版本控制。这并没有解决您描述的问题:2 个客户端永远不应该使用相同的会话令牌 - 这就是为什么会话令牌与设计良好的系统中的凭据分开的原因。

    【讨论】:

      猜你喜欢
      • 2013-12-18
      • 2019-09-15
      • 1970-01-01
      • 2021-01-04
      • 2015-06-16
      • 1970-01-01
      • 2021-06-22
      • 2017-12-13
      • 1970-01-01
      相关资源
      最近更新 更多