如何在另一个进程锁定表时暂停 Rebus 执行
How to suspend Rebus execution while another process is locking tables
我需要执行两个 API 来暂停和重新激活 Rebus 出队过程。
情况是服务总线Rebus和另一个软件共享同一个数据库和tables来存储应用程序数据(不是队列),但是后者软件(姑且称之为"Xprocess"), 在一天中的某个特定时刻,锁定一些 table 大约 3 分钟。由于 Xprocess 优先于 Rebus 消息执行,我们同意在此间隔期间通过 Xprocess 调用的 Suspend 和 Resume api 暂停 Rebus。
我正在徘徊哪种是暂停 Rebus 消息执行的最佳方式。
考虑到不可能添加暂停后续消息的特定消息,因为当 Xprocess 启动时,队列中可能已经有一些必须暂停的消息;
我的想法是在调用下一步之前在接收管道中添加一个询问 "Pausing" 服务的步骤。
像这样:
public class HandlePausingServiceStep : IIncomingStep
{
readonly ILog _log;
readonly IPausingService _pausingService;
public HandlePausingServiceStep(IRebusLoggerFactory rebusLoggerFactory, IPausingService pausingService)
{
_log = rebusLoggerFactory.GetLogger<HandleApplicationExceptionsStep>();
_pausingService = pausingService;
}
public async Task Process(IncomingStepContext context, Func<Task> next)
{
while (_pausingService.Pause)
Thread.Sleep(5000);
await next();
}
}
在做任何其他事情之前,我会考虑以下两个选项:
1) 只需在数据库锁定期间停止托管您的 Rebus 端点的进程。
如果您的端点托管为 Windows 服务(在许多情况下它可能应该如此),您可以简单地制作一个脚本 net stop YourService
/net start YourService
并使用 Windows' Task Scheduler 来完成它。
2) 使用Rebus的thread worker API 设置worker数量为0.
你可以这样做:
bus.Advanced.Workers.SerNumberOfWorkers(0);
然后是时候再次做事了:
bus.Advanced.Workers.SerNumberOfWorkers(5);
但显然您不应该从 Rebus 处理程序执行此操作(因为它将如何获得再次唤醒它的消息?:D)
就我个人而言,我更喜欢第一个选项,因为它非常简单并且易于操作人员管理。
更新:在意识到这个总线托管在 Web 应用程序的多个实例中之后,我想我会以稍微不同的方式解决这个挑战...
我认为 acceptable 的解决方案是在 Web 应用程序的后台设置一个计时器,定期检查数据库中特殊 table 中的配置标志值 - 这是可以做到的每 5-10 秒左右。
然后当标志被发出信号时,所有实例都会SetNumberOfWorkers(0)
,从而有效地停止所有消息处理。
当flag再次升起时,计时器可以重新添加worker。
如果在数据库锁定时不进行任何消息处理这一点很重要,那么在 raising/lowering 标志周围添加一点时间余量可能很重要。
我需要执行两个 API 来暂停和重新激活 Rebus 出队过程。
情况是服务总线Rebus和另一个软件共享同一个数据库和tables来存储应用程序数据(不是队列),但是后者软件(姑且称之为"Xprocess"), 在一天中的某个特定时刻,锁定一些 table 大约 3 分钟。由于 Xprocess 优先于 Rebus 消息执行,我们同意在此间隔期间通过 Xprocess 调用的 Suspend 和 Resume api 暂停 Rebus。
我正在徘徊哪种是暂停 Rebus 消息执行的最佳方式。 考虑到不可能添加暂停后续消息的特定消息,因为当 Xprocess 启动时,队列中可能已经有一些必须暂停的消息;
我的想法是在调用下一步之前在接收管道中添加一个询问 "Pausing" 服务的步骤。 像这样:
public class HandlePausingServiceStep : IIncomingStep
{
readonly ILog _log;
readonly IPausingService _pausingService;
public HandlePausingServiceStep(IRebusLoggerFactory rebusLoggerFactory, IPausingService pausingService)
{
_log = rebusLoggerFactory.GetLogger<HandleApplicationExceptionsStep>();
_pausingService = pausingService;
}
public async Task Process(IncomingStepContext context, Func<Task> next)
{
while (_pausingService.Pause)
Thread.Sleep(5000);
await next();
}
}
在做任何其他事情之前,我会考虑以下两个选项:
1) 只需在数据库锁定期间停止托管您的 Rebus 端点的进程。
如果您的端点托管为 Windows 服务(在许多情况下它可能应该如此),您可以简单地制作一个脚本 net stop YourService
/net start YourService
并使用 Windows' Task Scheduler 来完成它。
2) 使用Rebus的thread worker API 设置worker数量为0.
你可以这样做:
bus.Advanced.Workers.SerNumberOfWorkers(0);
然后是时候再次做事了:
bus.Advanced.Workers.SerNumberOfWorkers(5);
但显然您不应该从 Rebus 处理程序执行此操作(因为它将如何获得再次唤醒它的消息?:D)
就我个人而言,我更喜欢第一个选项,因为它非常简单并且易于操作人员管理。
更新:在意识到这个总线托管在 Web 应用程序的多个实例中之后,我想我会以稍微不同的方式解决这个挑战...
我认为 acceptable 的解决方案是在 Web 应用程序的后台设置一个计时器,定期检查数据库中特殊 table 中的配置标志值 - 这是可以做到的每 5-10 秒左右。
然后当标志被发出信号时,所有实例都会SetNumberOfWorkers(0)
,从而有效地停止所有消息处理。
当flag再次升起时,计时器可以重新添加worker。
如果在数据库锁定时不进行任何消息处理这一点很重要,那么在 raising/lowering 标志周围添加一点时间余量可能很重要。