我愿意设置一个长轮询Ajax调用来检查我的电子商务网络应用程序中的订单。在此应用程序中,客户能够在将来下订单的方式具有特殊性。因此,在管理面板中,我们有过去的订单和期货订单(将来可能是2个月或20分钟)。
基本上,我希望一旦未来的订单结束(未来的日期到达当前时间),就会警告后端的管理员用户。为了继续,我让用户admin执行Ajax调用(一旦他们连接到管理员)到服务器以检查要到达的期货订单。当调用等待服务器传递结果时,此Ajax调用是一个长轮询请求。如果服务器没有提供任何服务,则该请求将一直处于待处理状态,直到显示订单为止。
Ajax请求
(function poll() {
setTimeout(function() {
$.ajax({
url: '{{ path('commande_check') }}',
method: 'post',
success: function(r) {
if(r.ids) alert('New order!'); // I've simplified this part of the code to make it clean, admin are actually warned through Node.JS server
},
error: function() {},
complete: poll
});
}, 5000);
})();
{{path('commande_check')}} (由 Edit2 编辑)
public function checkAction(Request $request)
{
if($request->isXmlHttpRequest())
{
$response = new Response();
$em = $this->getDoctrine()->getManager();
$ids = array();
while(!$ids)
{
$ids = $em->getRepository('PaymentBundle:Commande')->findNewestOrders(new \DateTime());
if($ids)
break;
else
time_sleep_until(time() + self::SECONDS_TO_SLEEP);
}
if($ids)
{
return new JsonResponse(array(
'ids' => $ids
));
}
$response->setStatusCode(404);
return $response;
}
$response = new Response();
$response->setStatusCode(405);
return $response;
}
findNewestOrder()方法
public function findNewestOrders(\DateTime $datetime)
{
$query = $this->createQueryBuilder('c')
->select('c.id')
->leftJoin('Kt\PaymentBundle\Entity\Paiement', 'p', \Doctrine\ORM\Query\Expr\Join::WITH, 'p.id = c.paiement')
->andWhere('p.etat = 0')
->where("DATE_FORMAT(c.date, '%Y-%m-%d %H:%i') = :date")
->setParameter('date', $datetime->format('Y-m-d H:i'))
->andWhere('c.kbis IS NULL')
->andWhere('c.notif = 0')
->getQuery();
return $query->getArrayResult();
}
我的问题是警报有时永远不会显示,而数据库中的记录会更新。最奇怪的事情是它有时会发生,即使我离开页面进行Ajax调用就像它在后台运行一样。我认为问题来自time_sleep_until()
函数。我试过sleep(self::SECOND_TO_SLEEP)
,但问题是一样的。
任何帮助都会很高兴。谢谢!
修改1
我感觉与connection_status()
函数有关,因为即使用户已切换页面导致字段notif
在后台更新,while循环似乎仍会继续。
修改2
As per my answer,我设法克服了这种情况,但问题仍然存在。管理员确实正确地收到了通知。但是,我知道Ajax调用仍在继续进行请求。
我现在的问题是:会导致服务器资源过载吗? 我愿意在这个上获得赏金,因为我渴望知道实现我想要的最佳解决方案。
答案 0 :(得分:3)
我想我错了。
长时间轮询Ajax的目的是不,只有一个连接保持打开状态,例如websockets(正如我所想的那样)。一个人必须提出几个请求,但比常规民意调查要少得多。
Ajax常规轮询的目的是每隔2或3秒向服务器发出一次请求,以获得实时通知的外观。这些会在一分钟内导致许多Ajax调用。
由于服务器等待要将新数据传递给浏览器,因此每分钟只需要发出最少数量的请求。由于我每分钟都在数据库中检查新订单,使用长轮询可以让我将每分钟的请求数减少到1
。
因此,应用程序的特殊性使得使用Ajax长轮询不必要。只要对特定分钟进行了MySQL查询,就不需要在同一分钟内再次运行查询。这意味着我可以以60000毫秒的间隔进行定期轮询。此处也不需要使用sleep()
或time_sleep_until()
。
这是我最终如何做到的:
JS投票功能
(function poll() {
$.ajax({
url: '{{ path('commande_check') }}',
method: 'post',
success: function(r) {
if(r.ids)
alert('New orders');
},
error: function() {},
complete: function() {
setTimeout(poll, 60000);
}
});
})();
{{路径(' commande_check')}}
public function checkAction(Request $request)
{
if($request->isXmlHttpRequest())
{
$em = $this->getDoctrine()->getManager();
$ids = $em->getRepository('PaymentBundle:Commande')->findNewestOrders(new \DateTime());
if($ids)
{
return new JsonResponse(array(
'ids' => $ids
));
}
$response = new Response();
$response->setStatusCode(404);
return $response;
}
$response = new Response();
$response->setStatusCode(405);
return $response;
}
因此,我最终每分钟会收到一个检查新订单的请求。
感谢@SteveChilds,@ CherceK和@KevinB提出的善意建议。
答案 1 :(得分:1)
一般来说,这个问题有点粗糙。我们没有很多关于您的其他功能的信息。像findNewestOrders
...
我们可以假设它会提取管理员尚未完成的所有新订单,因此会显示。但是,如果它只查找完全相同的订单,它们将永远不会被填充。
理论上,如果没有提交新订单,这将永远运行。您没有时间限制,因此服务器可能感觉您遇到while
永远不会false
并执行超出执行时间的情况。
根据您的评论
Returns TRUE on success or FALSE on failure.
它失败的唯一方法是,如果函数本身失败或某些服务器端问题导致失败返回。由于您从未正式访问该页面,并且没有让您的ajax页面提交失败响应的行为,因此它永远不会真正失败。
我认为考虑为此做一个CRON工作可能更明智,并且有一个您查询的不完整订单的数据库。 CRON可以每分钟运行并填充数据库。服务器上的运行不会那么好,因为它很可能不会超过30秒。
对于许多功能而言,长轮询可能是一个好主意,但我并不完全相信它就是这种情况。我会认真推荐setInterval作为服务器上的负载,并且客户端在每分钟或每两分钟30秒的呼叫中不会那么好。这是你最后的电话。
答案 2 :(得分:1)
我个人会频繁检查,而不是有一个运行很长时间的请求 - 这不是很理想的长期运行这样的流程,因为它们会占用服务器连接,实际上,它只是不好的做法。此外,浏览器可能会将连接时间排除在外,这就是您可能看不到预期响应的原因。
您是否尝试过更改ajax调用,以便每隔60秒(或者经常需要)调用,检查自上次轮询以来的新订单(只需在页面/ HTML5中跟踪它)本地存储,因此它会持续跨页面并在ajax请求中作为参数传递它,然后只返回是否有新订单的指示,或者没有没有'
如果有新订单,您可以显示消息。
答案 3 :(得分:0)
我终于设法克服了这个错误,但没有深入研究这个问题。
我已将用于更新notif
字段的代码与获取新订单的代码分开。这样,while
循环仍然继续,但无法更新字段。
因此,通过发出新的ajax请求来更新字段,可以在第一个ajax调用成功时更新该字段。因此,管理员始终会收到通知。
我只需要查询内存/线程级别,看看这个循环使用了多少资源消耗。
由于没有找到任何解决方案,因为最初的错误导致了我的解决方法,因为问题仍然存在,我不会接受我的回答。
非常感谢有关该问题的所有帮助。