在 while() 循环中使用 Ajax 和 sleep()/time_sleep_until() 进行长轮询
Long polling with Ajax with sleep()/time_sleep_until() in while() loop
我愿意设置长轮询 Ajax 调用来检查我的电子商务网络应用程序中的订单。此应用程序的特殊性在于客户能够在未来下订单的方式。因此,在管理面板中,我们有过去的订单和期货订单(可以是 2 个月或 20 分钟后)。
基本上,我希望后台的管理员用户在未来订单结束时(未来日期到达当前时间)立即收到警告。为了继续,我让用户管理员对服务器进行 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);
})();
{{ 路径('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
,我设法克服了这种情况,但问题仍然存在。管理员确实收到了正确的通知。但是,我知道 Ajax 调用在请求发出后仍在继续。
我现在的问题是:这会导致服务器资源过载吗?
我愿意为此悬赏,因为我很想知道实现我想要的目标的最佳解决方案。
总的来说这个问题说起来有点粗糙。关于您的其他功能的确切功能,我们没有太多信息。喜欢 findNewestOrders
...
我们可以假设它会拉取管理员尚未完成的所有新订单,因此将显示出来。但是,如果它只查找完全相等的订单,它们将永远不会被执行。
理论上,如果没有提交新订单,这将永远 运行。您对此没有时间限制,因此服务器可能会觉得您遇到了 while
永远不会 false
并执行超过执行时间的情况。
根据您的评论
Returns TRUE on success or FALSE on failure.
唯一会失败的方法是函数本身失败或某些服务器端问题导致失败 return。由于您从未正式访问该页面,并且离开您的 ajax 页面的任何行为都不会提交失败响应,因此它永远不会真正失败。
我认为考虑为此做一个 CRON 作业并拥有一个您查询的不完整订单的数据库可能更明智。 CRON 可以每分钟 运行 填充数据库。服务器上的 运行 不会那么好,因为无论如何它很可能不会超过 30 秒。
长轮询对于许多函数来说可能是个好主意,但我不完全相信它确实如此。我会认真推荐 setInterval,因为服务器和客户端上的负载在每分钟或两分钟的 30 秒调用中不会那么大。终究是你的决定。
我个人会经常检查,而不是让一个请求运行很长时间 - 拥有像这样的 运行 长进程并不理想,因为它们会占用服务器连接,实际上,这只是一种不好的做法.此外,浏览器可能会超时连接,这就是为什么您可能看不到预期的响应的原因。
您是否尝试过更改 ajax 调用,例如每 60 秒调用一次(或者您希望的任何频率),检查自上次轮询以来的新订单(只需跟踪这在页面 / HTML5 本地存储中,因此它跨页面持续存在并将其作为参数传递到 ajax 请求中)然后简单地 returns 表示是的,有新订单,还是没有?
如果有新订单,您可以显示一条消息。
我终于克服了这个错误,但没有深入挖掘问题。
我已将更新 notif
字段的代码与获取新订单的代码分开。这样,while
循环仍然继续但无法更新字段。
因此,在第一次 ajax 调用成功时,通过发出新的 ajax 请求来更新该字段,从而更新该字段。因此,管理员总是会收到通知。
我只需要在 memory/thread 级别查询即可了解此循环使用的资源消耗量。
尽管我解决了最初的错误,但仍未找到解决方案,我不会接受我的回答,因为问题仍然存在。
非常感谢您对这个问题的所有帮助。
我想我全错了。
长轮询 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、@CayceK 和@KevinB 的建议。
我愿意设置长轮询 Ajax 调用来检查我的电子商务网络应用程序中的订单。此应用程序的特殊性在于客户能够在未来下订单的方式。因此,在管理面板中,我们有过去的订单和期货订单(可以是 2 个月或 20 分钟后)。
基本上,我希望后台的管理员用户在未来订单结束时(未来日期到达当前时间)立即收到警告。为了继续,我让用户管理员对服务器进行 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);
})();
{{ 路径('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
我现在的问题是:这会导致服务器资源过载吗? 我愿意为此悬赏,因为我很想知道实现我想要的目标的最佳解决方案。
总的来说这个问题说起来有点粗糙。关于您的其他功能的确切功能,我们没有太多信息。喜欢 findNewestOrders
...
我们可以假设它会拉取管理员尚未完成的所有新订单,因此将显示出来。但是,如果它只查找完全相等的订单,它们将永远不会被执行。
理论上,如果没有提交新订单,这将永远 运行。您对此没有时间限制,因此服务器可能会觉得您遇到了 while
永远不会 false
并执行超过执行时间的情况。
根据您的评论
Returns TRUE on success or FALSE on failure.
唯一会失败的方法是函数本身失败或某些服务器端问题导致失败 return。由于您从未正式访问该页面,并且离开您的 ajax 页面的任何行为都不会提交失败响应,因此它永远不会真正失败。
我认为考虑为此做一个 CRON 作业并拥有一个您查询的不完整订单的数据库可能更明智。 CRON 可以每分钟 运行 填充数据库。服务器上的 运行 不会那么好,因为无论如何它很可能不会超过 30 秒。
长轮询对于许多函数来说可能是个好主意,但我不完全相信它确实如此。我会认真推荐 setInterval,因为服务器和客户端上的负载在每分钟或两分钟的 30 秒调用中不会那么大。终究是你的决定。
我个人会经常检查,而不是让一个请求运行很长时间 - 拥有像这样的 运行 长进程并不理想,因为它们会占用服务器连接,实际上,这只是一种不好的做法.此外,浏览器可能会超时连接,这就是为什么您可能看不到预期的响应的原因。
您是否尝试过更改 ajax 调用,例如每 60 秒调用一次(或者您希望的任何频率),检查自上次轮询以来的新订单(只需跟踪这在页面 / HTML5 本地存储中,因此它跨页面持续存在并将其作为参数传递到 ajax 请求中)然后简单地 returns 表示是的,有新订单,还是没有?
如果有新订单,您可以显示一条消息。
我终于克服了这个错误,但没有深入挖掘问题。
我已将更新 notif
字段的代码与获取新订单的代码分开。这样,while
循环仍然继续但无法更新字段。
因此,在第一次 ajax 调用成功时,通过发出新的 ajax 请求来更新该字段,从而更新该字段。因此,管理员总是会收到通知。
我只需要在 memory/thread 级别查询即可了解此循环使用的资源消耗量。
尽管我解决了最初的错误,但仍未找到解决方案,我不会接受我的回答,因为问题仍然存在。
非常感谢您对这个问题的所有帮助。
我想我全错了。
长轮询 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、@CayceK 和@KevinB 的建议。