在正在运行的RabbitMQ服务器用户PHP上,
我有一个事件会触发一条消息"将用户X帐户带到5美元"到"充电"队列。但是,许多事件可能会发送此信息,如果有多个工作人员在系统上工作,我可以看到一个问题,即在用户获得双倍费用的情况下会发生竞争条件。
$accountBalance = -25.00 // starts
event 1: "Bring User X Account to $5"
event 1: check balance, $balance = -25.00
event 2: "Bring User X Account to $5"
event 1: $chargeAmount = 30
event 2: check balance, $balance = -25.00
event 1: Charge Card $30 // takes 3s
event 2: $chargeAmount = 30
event 2: Charge Card $30 // takes 6s
event 3: "Bring User X Account to $5"
event 3: check balance, $balance = -25.00
event 1: add transaction $30 // $accountBalance = 5.00
event 1: END
event 4: "Bring User X Account to $5"
event 4: check balance, $balance = 5.00
event 4: END
event 2: add transaction $30 // $accountBalance = 35.00
event 2: END
event 3: $chargeAmount = 30
event 3: Charge Card $30 // takes 3s
event 3: add transaction $30 // $accountBalance = 65.00
event 2: END
$accountBalance = 65.00
正如你所看到的,有4个事件进入,而不是一次收费30美元,它收费三次。只有事件4退出,但因为事件1很快就结束了。
我玩过锁定的想法,但由于每个工作人员可能在一个独立的服务器上,这有点模糊。
是否有任何易于使用的开箱即用解决方案?
答案 0 :(得分:1)
您需要使用分布式锁定来防止两个任务同时发生,同时使您的任务具有幂等性。即使您确实有锁,任务也需要能够运行并检测工作已完成并忽略。这两个概念共同发挥作用。
您可以使用memcached之类的内容进行分布式锁定。例如,您可以尝试LockBundle之类的内容。
$memcached = new Memcached();
$this->adapter = new MemcachedAdapter($memcached);
$lock = new Lock("test", $this->adapter);
$lock->acquire();
// Do stuff here //
$lock->release();
只需将锁密钥设置为您想要保护的粒度即可。如果您正在尝试为memcached崩溃做出一些有弹性的东西,那么HA有一些问题。
您还可以构建类似的东西,使用数据库表,您在字段上有PK并让进程尝试在表中插入值。 PK将阻止任何第二次尝试,并且一旦第一次尝试提交,后续的将由于PK违规而解除阻塞并失败。
然后可能会使用像Zookeeper这样的东西,虽然我从来没有这样做过。这比我想的memcached更有弹性。