4

我们有一个 Web 应用程序(它是一个游戏),其中包含许多各种形式和元素,它们充当按钮并在服务器上触发一些操作。问题是,如果用户点击按钮太快或在两个选项卡中打开网站然后同时发出一些操作,用户有时会混淆我们的应用程序。我们有一些基本的保护——MySQL 事务,一些双击防止 Javascripts,但无论如何有时有些东西会跳过。当然,最好的方法是重新设计所有的 SQL 事务和支持功能,使其不会混淆系统。这种混乱的一个例子是同时发布两个更新 - 一个 Web 请求更改了数据库中的某些内容,但第二个请求仍然使用旧数据运行,因此 SQL 更新返回“受影响的行数为零” 因为第一个事务已经更改了数据库中的数据。显而易见的解决方案是在 UPDATE 之前再次读取数据以查看它是否仍需要更新,但这意味着在各处放置更多的双重 SELECT 查询,这不是一个好的解决方案 - 为什么要从 db 读取相同的数据两次?我们还考虑使用一些隐藏的令牌在服务器上比较每个更新请求并拒绝具有相同令牌 id 的操作,但这也意味着要触及很多代码位置,并可能将新错误引入系统,除了工作正常之外这一个问题。不是一个好的解决方案 - 为什么要从 db 读取相同的数据两次?我们还考虑使用一些隐藏的令牌在服务器上比较每个更新请求并拒绝具有相同令牌 id 的操作,但这也意味着要触及很多代码位置,并可能将新错误引入系统,除了工作正常之外这一个问题。不是一个好的解决方案 - 为什么要从 db 读取相同的数据两次?我们还考虑使用一些隐藏的令牌在服务器上比较每个更新请求并拒绝具有相同令牌 id 的操作,但这也意味着要触及很多代码位置,并可能将新错误引入系统,除了工作正常之外这一个问题。

动作流的逻辑如下:如果用户同时发出两个请求,第二个请求应该等到第一个请求完成。但是我们还必须考虑到我们的应用程序中有很多重定向(例如在 POST 之后以避免用户刷新页面时重复 POST),因此该解决方案不应为用户造成死锁。

所以问题是:什么是最简单的全局修复,它可以以某种方式使所有用户操作按顺序进行?有什么好的通用解决方案吗?

我们正在使用 MySQL 和 PHP。

4

3 回答 3

2

我很高兴您意识到这只是一个糟糕的临时解决方案,您应该优化您的代码。忘记你的代币和东西。最简单且仍然有效的方法可能是每次操作都对共享文件进行排他文件锁定。你可以把它想象成一块木头,只有一个人可以持有 id,只有持有它的人才能说话或做某事。

<?php
    $fp = fopen("/tmp/only-one-bed-available.txt", "w+");

    if (flock($fp, LOCK_EX)) { // do an exclusive lock

         // do some very important critical stuff here which must not be interrupted:
         // sleeping.
         sleep(60);
         echo "I now slept 60 seconds";
        flock($fp, LOCK_UN); // release the lock
    } else {
        echo "Couldn't get the lock!";
    }

    fclose($fp);
?>

如果您并行执行此脚本 10 次,则需要 10 分钟才能完成(因为只有一张床可用!)并且您将每 60 秒(大约)看到一次回显“我现在睡觉了……”。

这会在全球范围内序列化此代码的所有执行。这可能不是您想要的(您希望基于每个用户,不是吗?)我相信您有类似用户 ID 的东西,否则使用外国 IP 地址,并且每个用户都有一个唯一的文件名:

<?php $fp = fopen("/tmp/lock-".$_SERVER["REMOTE_ADDR"].".txt", "w+"); ?>

您还可以为一组操作定义锁定文件名。也许用户可以并行做一些事情,但只有这个操作会产生问题?给它一个标签并将其包含在他的锁定文件名中!

这个功法还真不错,可以用!只有很少的方法可以更快地做到这一点(mysql内部,共享内存段,更高层的序列化,如负载均衡器......)

使用上面发布的解决方案,您可以在您的应用程序中执行此操作,这可能很好。您也可以在 Mysql 中使用相同的方案:http: //dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_get-lock

但是对于您的用例,PHP 实现可能更容易更好。

如果你真的想要一个更优雅的解决方案,只需查看这个函数http://www.php.net/manual/en/function.sem-get.php

于 2010-09-23T18:41:09.127 回答
1

两个选项卡和同时操作?您的用户有多快?

无论如何:只允许一个选项卡已经是一个很好的解决方案。

只需一个选项卡,您就可以在 javascript 中堆叠操作,并在获得最后一次调用的返回值后提交一个新的操作。

于 2010-09-23T16:29:32.517 回答
1

使用mysql的get_lock函数怎么样?另外也许你应该看看 mysql's transactions

于 2010-09-23T18:56:26.187 回答