Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/actionscript-3/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
最可靠&;PHP中防止竞争条件的安全方法_Php_Concurrency - Fatal编程技术网

最可靠&;PHP中防止竞争条件的安全方法

最可靠&;PHP中防止竞争条件的安全方法,php,concurrency,Php,Concurrency,我需要在PHP中使用互斥体或信号量,这让我很害怕。为了澄清这一点,我并不害怕编写正确同步的无死锁代码,也不害怕并发编程的危险,而是担心PHP处理边缘情况的能力 快速背景:编写一个位于用户和第三方信用卡网关之间的信用卡处理程序界面。需要防止重复请求,并且已经有了一个可以工作的系统,但是如果用户在毫秒之间点击submit(启用了w/out JS,所以我不能为它们禁用按钮),则会出现竞态条件,我的PHP脚本没有意识到已经发出了重复请求。需要一个信号量/互斥量,这样我就可以确保每个唯一事务只通过一个成功

我需要在PHP中使用互斥体或信号量,这让我很害怕。为了澄清这一点,我并不害怕编写正确同步的无死锁代码,也不害怕并发编程的危险,而是担心PHP处理边缘情况的能力

快速背景:编写一个位于用户和第三方信用卡网关之间的信用卡处理程序界面。需要防止重复请求,并且已经有了一个可以工作的系统,但是如果用户在毫秒之间点击submit(启用了w/out JS,所以我不能为它们禁用按钮),则会出现竞态条件,我的PHP脚本没有意识到已经发出了重复请求。需要一个信号量/互斥量,这样我就可以确保每个唯一事务只通过一个成功的请求

我通过PHP-FPM在nginx后面运行PHP,在多核Linux机器上有多个进程。我想确定

  • 信号量在所有php fpm进程之间以及在所有内核(i686内核)之间共享
  • php fpm在持有互斥/信号量时处理php进程崩溃,并相应地释放它
  • php fpm在保持互斥/信号量的同时处理会话中止,并相应地释放它
  • 是的,我知道。非常基本的问题,如果认为任何其他软件都不存在合适的解决方案,那将是愚蠢的。但这就是PHP,它肯定不是在考虑并发性的情况下构建的,它经常崩溃(取决于您加载的扩展),并且处于一个不稳定的环境中(PHP-FPM和web)

    关于(1),我假设如果PHP使用POSIX函数,那么这两个条件在SMPI686机器上都成立。至于(2),我从简要浏览文档中看到,有一个参数决定了这种行为(尽管我不明白,如果会话被终止,为什么会希望PHP不释放互斥锁)。但是(3)是我主要关心的问题,我不知道假设php fpm为我正确地处理了所有附带的情况是否安全。我(显然)不希望出现死锁,但我不确定我是否可以相信PHP不会让我的代码处于无法获得互斥的状态,因为捕获它的会话被正常或不正常地终止

    我曾考虑过使用MySQL
    锁表方法,但这一点更值得怀疑,因为尽管我更信任MySQL锁而不是PHP锁,但我担心如果PHP在保持MySQL会话锁的同时中止请求(带*out*崩溃),MySQL可能会保持表锁定(特别是因为我可以很容易地想象出导致这种情况发生的代码)

    老实说,我最喜欢一个非常基本的C扩展,在这里我可以确切地看到POSIX调用是什么,以及使用什么参数来确保我想要的确切行为……但我并不期待编写这段代码


    任何人都有关于PHP的与并发相关的最佳实践,他们想与大家分享吗?

    如果问题只在相隔几毫秒点击一个按钮时出现,那么软件去Bouncer难道不起作用吗?比如说,在会话变量中节省按下按钮的时间,不允许任何时间,比如说,一秒钟?就在我早晨喝咖啡之前。Cheers.

    您可以将随机哈希存储在会话数据的数组中,并将该哈希打印为隐藏表单输入值。当请求传入时,如果会话数组中存在隐藏哈希值,则可以从会话中删除该哈希并处理表单,否则不可以


    这将防止重复表单提交,并有助于防止csrf攻击。

    事实上,我认为不需要复杂的互斥/信号量解决方案

    存储在PHP
    $\u会话中的表单键就是您所需要的。作为一个很好的副作用,此方法还可以保护您的表单免受CSRF攻击

    在PHP中,会话被锁定,
    session\u start()
    等待用户会话被释放。您只需
    unset()
    第一个有效请求上的表单键。第二个请求必须等待第一个请求释放会话

    但是,当运行在(不是基于会话或源ip的)负载平衡场景中时,事情变得越来越复杂。对于这种场景,我相信您会在这篇伟大的文章中找到一个有价值的解决方案:

    我通过以下演示复制了您的用例。只需将此文件放到您的Web服务器上并进行测试:

    <?php
    session_start();
    if (isset($_REQUEST['do_stuff'])) {
      // do stuff
      if ($_REQUEST['uniquehash'] == $_SESSION['uniquehash']) {
        echo "valid, doing stuff now ... "; flush();
        // delete formkey from session
        unset($_SESSION['uniquehash']);
        // release session early - after committing the session data is read-only
        session_write_close();
        sleep(20);  
        echo "stuff done!";
      }
      else {
        echo "nope, {$_REQUEST['uniquehash']} is invalid.";
      }     
    }
    else {
      // show form with formkey
      $_SESSION['uniquehash'] = md5("foo".microtime().rand(1,999999));
    ?>
    <html>
    <head><title>session race condition example</title></head>
    <body>
      <form method="POST">
        <input type="hidden" name="PHPSESSID" value="<?=session_id()?>">
        <input type="text" name="uniquehash" 
          value="<?= $_SESSION['uniquehash'] ?>">
        <input type="submit" name="do_stuff" value="Do stuff!">
      </form>
    </body>
    </html>
    <?php } ?>
    
    
    会话竞争条件示例
    
    你有一个有趣的问题,但你没有任何数据或代码显示

    在80%的情况下,如果您遵循关于阻止用户多次提交表单的标准过程和实践,那么由于PHP本身而发生任何不愉快事件的可能性几乎为零,这几乎适用于所有其他设置,而不仅仅是PHP


    如果您是20%的用户,并且您的环境要求这样做,那么其中一个选项就是使用消息队列,我相信您对此很熟悉。同样,这种想法与语言无关。与语言无关。这一切都是关于数据如何移动的。

    为了防止代码中出现会话争用情况,我所做的是在存储的最后一个操作之后会话中的数据我使用PHP函数请注意,如果您使用的是PHP7,则需要禁用PHP.ini中的默认输出缓冲。如果您有耗时的操作,最好在调用会话_write_close()后执行这些操作


    我希望它能帮助别人,对我来说它救了我的命:)

    我不熟悉你的设置,但当我看到你需要阻止重复请求被处理时,我想知道为什么你不能检查存储在用户会话中的。这基本上就是我现有的重复请求预防功能,似乎有一个特定的时间点,对于具有多个PHP进程的特定设置,存在两个PHP处理后端的竞争条件。请参见token/nonce/w