java - 大量单线程任务队列

标签 java concurrency redis distributed task-queue

在我们公司,我们有一个分布在几个实例中的服务器。服务器处理用户请求。来自不同用户的请求可以并行处理。来自相同用户的请求应该严格顺序执行。但是由于平衡,它们可以到达不同的实例。目前我们使用基于 Redis 的分布式锁,但这很容易出错,并且需要比业务逻辑更多的并发工作。

我想要的是这样的东西(更像是一个概念):

  • 每个用户的不同队列
  • 队列以用户id命名
  • 每个请求都由请求 ID 标识
  • 想象一下来自同一用户的两个请求同时到达两个不同的实例:

    1. 每个实例将它们的请求 ID 放入此用户队列。
    2. 此外,他们都在本地存储他们的请求 ID。
    3. 然后一些代理从“some_user_queue”的顶部获取请求 ID 并将其移动到“some_user_queue_processing”
    4. 两个实例都监听“some_user_queue_processing”。他们查看它,看看这是否是他们存储在本地的请求 ID。如果是,则进行处理。如果不是,则忽略并等待。
    5. 工作完成后,服务器会从“some_user_queue_processing”中删除此 ID。
    6. 然后再次执行第 3 步。

所有这一切同时发生在很多(数以千计的)不同用户(及其队列)中。

现在,我知道这听起来很像 Actor ,但是:

  1. 我们需要尽可能少更改的解决方案,以实现从锁定的快速转换。 Akka 将迫使我们从头开始重写几乎所有内容。
  2. 我们需要生产就绪的解决方案。 Quasar 听起来不错,但尚未准备好投入生产(更准确地说,是他们的 Galaxy 集群)。
  3. 我工作的高层非常保守,他们根本不想要我们需要支持的另一个依赖项。但我们已经在使用 Redis(用于分布式锁),所以我想它也许也能帮助解决这个问题。

谢谢

最佳答案

与您的问题描述相匹配的最佳解决方案是 Redis Cluster .

基本上,集群通过以下方式解决了您的并发问题:

来自同一用户的两个(或更多)请求将总是转到同一个实例,假设您使用用户 ID 作为键并将请求作为值。该值实际上必须是 list的请求。当您收到一个时,您会将其附加到该列表中。换句话说,这是您的请求队列(每个用户一个)。

集群实现的设计使这种匹配成为可能。它基于分布在所有实例上的一系列哈希槽。

当执行 set 命令时,集群会执行哈希操作,这会产生一个位于特定实例上的值(我们要写入的哈希槽) .集群找到包含正确范围的实例,然后执行写入过程。

此外,当执行get 时,集群会执行相同的过程:找到包含键的实例,然后获取值。

从锁转换非常容易执行,因为您只需要准备好实例(将 cluster-enabled 指令设置为“yes”),然后从 redis-trib.rb 脚本运行 cluster-create 命令.

去年夏天,我在生产环境中使用过该集群,它运行良好。

关于java - 大量单线程任务队列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33457390/

相关文章:

sql - 涉及子选择和外键的 Postgres 竞争条件

node.js - Docker:使用 docker run 而不是 docker-compose up 时无法连接到 Redis

Redis SCAN 命令返回与模式不匹配的键

java - 命令行 Java + Jar 文件

java - 具有 LDAP 身份验证的自定义权限

java - 如何将 Jersey ExceptionMapper 与 Google Guice 一起使用?

java - 用 Java 实现 Peterson 锁

java - 双整数转换困惑

ios - 对于 iOS,Concurrency Programming Guide 和 Threading Programming Guide 有什么区别?

java - redis.clients.jedis.exceptions.JedisConnectionException : Could not get a resource from the pool while connecting to redis cluster