允许所有工作人员使用全球消耗率的队列服务器

时间:2019-06-23 15:24:17

标签: java algorithm message-queue amqp

我的服务器需要处理许多任务。由于工作人员需要满足的API调用速率限制,这些任务必须以特定的给定速率处理。

为了确保不以高于API速率限制的速率执行这些任务,我希望能够配置队列发送消息以进行处理的速率。

此外,该队列还必须保持推送消息的顺序,并以FIFO顺序释放它们以提供公平性。

最后,如果很好的编码方式,使用时将是透明的,那就太好了,这样客户端将通过API调用将消息发送到队列,并且相同的客户端将在消息被释放后接收回该消息。根据工作率和相关顺序排队。例如使用RxJava

waitForMessageToBeReleased(message, queue)
     .subscribe(message -> // do some stuff)  // message received to the same 
client after it was released by the queue according to the defined work rate.

我目前正在使用Redis通过创建一个具有特定TTL数量的变量来控制执行速度,并且其他调用会等到该变量到期为止。但是,它不能处理订单,并且可能导致客户在高负载情况下饿死。

1 个答案:

答案 0 :(得分:0)

Cadence Workflow能够以最小的努力支持您的用例。

这是可以满足您要求的稻草人设计:

  • 使用userID作为工作流ID向用户工作流发送signalWithStart请求。它要么向工作流程传递信号,要么首先启动工作流程并向其传递信号。
  • 对该工作流的所有请求均由它缓冲。 Cadence严格保证打开状态下只能存在一个具有给定ID的工作流。因此,可以保证所有信号(事件)都将在属于用户的工作流中进行缓冲。
  • 内部工作流程事件循环逐个分发这些请求。
  • 当缓冲区为空时,工作流可以完成。

以下是用Java实现它的工作流程代码(也支持Go客户端):

public interface SerializedExecutionWorkflow {

    @WorkflowMethod
    void execute();

    @SignalMethod
    void addTask(Task t);
}

public interface TaskProcessorActivity {
    @ActivityMethod
    void process(Task poll);
}

public class SerializedExecutionWorkflowImpl implements SerializedExecutionWorkflow {

    private final Queue<Task> taskQueue = new ArrayDeque<>();
    private final TaskProcesorActivity processor = Workflow.newActivityStub(TaskProcesorActivity.class);

    @Override
    public void execute() {
        while(!taskQueue.isEmpty()) {
            processor.process(taskQueue.poll());
        }
    }

    @Override
    public void addTask(Task t) {
        taskQueue.add(t);
    }
}

然后通过信号方法将任务排队到工作流中的代码:

private void addTask(WorkflowClient cadenceClient, Task task) {
    // Set workflowId to userId
    WorkflowOptions options = new WorkflowOptions.Builder().setWorkflowId(task.getUserId()).build();
    // Use workflow interface stub to start/signal workflow instance
    SerializedExecutionWorkflow workflow = cadenceClient.newWorkflowStub(SerializedExecutionWorkflow.class, options);
    BatchRequest request = cadenceClient.newSignalWithStartRequest();
    request.add(workflow::execute);
    request.add(workflow::addTask, task);
    cadenceClient.signalWithStart(request);
}

与使用队列进行任务处理相比,Cadence具有许多其他优点。

  • 构建具有无限到期间隔的指数重试
  • 故障处理。例如,它允许执行一个任务,如果在配置的时间间隔内两次更新均未成功,则该任务会通知另一服务。
  • 支持长时间运行的心跳操作
  • 能够实现复杂的任务依赖性。例如,在无法恢复的故障(SAGA)的情况下实现呼叫链或补偿逻辑的链接
  • 完全了解更新的当前状态。例如,当使用队列时,您就会知道队列中是否有某些消息,并且需要其他数据库来跟踪总体进度。使用Cadence可以记录每个事件。
  • 能够取消正在进行的更新。
  • 分布式CRON支持

请参见介绍Cadence编程模型的the presentation