自动更新系统,如何避免多次续订?

时间:2015-12-02 16:38:14

标签: php concurrency race-condition

我提供的服务可以自动向客户收取费用,以便在帐户达到特定阈值时续订其信用额度。它的工作方式与Mandrill相同。

代码级别,其工作方式如下:当用户进行查询时,应用会将其信用减少为1,然后检查当前信用== {自动续订金额}。如果是这种情况,则会将作业发送到队列服务以开始续订。

测试在这里至关重要,我检查何时保持EQUALS是自动续订的值,而不是当它是低或等时,因为在这种情况下,每次用户查询API时都会进行更新而第一次自动续订尚未完全处理。

但我今天有一个奇怪的问题。我的一个客户有两个续订而不是一个。我的第一个猜测是API上存在竞争条件,这意味着两个请求同时进行,其中"剩余的"信用额度相同,等于自动续订的数量,从而投入两次自动续订。

我的问题很简单:如何避免再续约?

主要思想是解决竞争条件问题,但我不知道如何使用NGinx用PHP编写API。服务器应该知道对同一个帐户有N个请求,以便计算信用的当前使用情况(剩余 - {并发请求}),这似乎是不可能的。

我该怎么做?

2 个答案:

答案 0 :(得分:2)

解决方案是只允许1个PHP进程在任何给定时间运行自动充电功能。最简单的方法是使用$('.myform').submit(function(e) { var $this = $(this); $.ajax({ type: "GET", // GET & url for json slightly different url: "http://XXXXXXXX.list-manage2.com/subscribe/post-json?c=?", data: $this.serialize(), dataType : 'json', contentType: "application/json; charset=utf-8", error : function(err) { alert("Could not connect to the registration server."); }, success : function(data) { if (data.result != "success") { // Something went wrong, parse data.msg string and display message } else { // It worked, so hide form and display thank-you message. } } }); return false; });

包围您的代码
flock()/fclose()

这将确保一次只运行一个PHP进程$lock = fopen("/tmp/renew", "w+"); flock($lock, LOCK_EX); <renew code> fclose($lock); 。因此,即使有100个续订请求,请求#1将运行续订代码,请求#2-#100将在<renew code>时停止运行。当#1结束时,#2将运行,#2结束,#3运行,依此类推。

答案 1 :(得分:1)

我终于找到了这个解决方案:

由于从排队系统(Beanstalk)调用续订脚本,并且由于我的服务器只运行此续订脚本的一个实例,因此如果需要续订信用,我会再次检查续订脚本。

假设有两个同一个帐户的调用,第一个将返回比auto_renew数量少的信用,从而添加新的信用。 第二轮将看到现在有更多的信用额度而不是auto_renew数量,并且将被忽略。

我认为我已经解决了这个问题,但我更倾向于向@Brian提出一个明智的方法,以防万一脚本没有从排队系统中调用!