7

一些背景,在真正的问题之前:

我正在开发一个由几个不同模块组成的后端应用程序。目前,每个模块都是一个命令行 java 应用程序,它是“按需”运行的(稍后会详细介绍)。

每个模块都是一个“步骤”,是您可以将其视为数据流的更大流程的一部分;第一步从外部源收集数据文件并将它们推送/加载到一些 SQL 数据库表中;然后以下步骤,根据不同的条件和事件(时间、数据库中数据的存在、通过 Web 服务/Web 接口完成的消息和详细说明),从(1 个或多个)数据库表中获取数据,处理它们,并将它们写在不同的桌子上。步骤在三个不同的服务器上运行,并从三个不同的 DB 读取数据,但仅写入单个 DB。目的是聚合数据、计算指标和统计数据。

目前,每个模块都是定期执行的(从第一个模块的几分钟/小时,到链中最后一个模块的几天,这需要聚合更多数据,因此等待它们“更长”时间才能可用),使用定时任务。一个模块(目前是一个 java 控制台应用程序)正在运行,它检查数据库在给定的日期时间窗口中是否有新的、未处理的信息,并完成它的工作。

问题:它有效,但是.. 我需要扩展和维护它,这种方法开始显示其局限性。

  1. 我不喜欢依赖“投票”;这是一种浪费,考虑到先前模块的信息可能足以“告诉”链条下游的其他模块,当它们需要的信息可用时,它们可以继续进行。
  2. 它是“慢”的:链下模块的几天延迟是存在的,因为我们必须确保数据到达并由之前的模块处理。所以我们“停止”这些模块,直到我们确定我们拥有所有数据。新增内容需要对某些指标进行实时(不难,但“尽快”)计算。一个很好的例子是这里发生的事情,在 SO 上,有徽章!:) 我需要获得一些非常相似的东西。

为了解决第二个问题,我将介绍“部分”或“增量”计算:只要我有一组相关信息,我就会对其进行处理。然后,当一些其他链接信息到达时,我计算差异并相应地更新数据,但我还需要通知其他(依赖)模块。

问题

- 1) 最好的方法是什么?- 2)相关:这是“通知”其他模块(在我的情况下是java可执行文件)相关数据可用的最佳方式?

我可以看到三种方式:

  • 将其他“非数据”表添加到数据库中,每个模块在其中写入“嘿,我已经完成了这个并且它是可用的”。当 cronjob 启动另一个模块时,它读取表,决定他可以计算子集 xxx,然后执行它。等等
  • 使用消息队列,如 ZeroMQ,(或 Apache Camel,如建议的@mjn)而不是 DB 表
  • 使用键值存储,例如 Redis,而不是 DB 表

编辑:我确信基于队列的方法是可行的方法,我添加了“表+轮询”选项以确保完整性,但现在我明白这只是一种干扰(显然,每个人都会回答“是的,使用队列,投票是邪恶的”——这是正确的!)。因此,让我将问题重新表述为: 与 Redis 之类的带有 pub/sub 的键值存储相比,使用 MQ 有哪些优点/缺点?

  • 3)有什么解决方案可以帮助我完全摆脱 cronjobs 吗?

编辑:特别是,在可能的情况下,这意味着:在某些 MQ 和/或键值存储中是否有一种机制可以让我发布带有“时间”的消息?比如“1天送达”?有了坚持和“几乎一次”的交付保证,显然

  • 4) 我是否应该将此基于消息(事件?)的解决方案构建为集中式服务,在其中一台服务器上将其作为守护程序/服务运行?
  • 5)我是否应该放弃按需启动订阅者的想法,让每个模块作为守护程序/服务连续运行?
  • 6) 有哪些优点和缺点(可靠性、单点故障与资源使用和复杂性......)?

编辑:这是我最关心的一点:我想自己“排队”以根据队列中的消息激活“模块”,类似于 MSMQ 激活。这是个好主意吗?Java 世界中有什么可以做到的吗,我应该自己实现它(通过 MQ 还是通过 Redis),还是应该将每个模块作为守护进程运行?(即使某些计算通常是突发的,两个小时的处理,然后是两天的空闲时间?)

注意:我不能使用重型容器/EJB(没有 Glassfish 或类似的)

编辑:骆驼对我来说似乎也有点太重了。我在这里寻找真正轻松的东西,无论是在资源方面还是在开发的复杂性方面

4

3 回答 3

1

队列任务描述部分听起来像基于“企业集成模式”的系统,如Apache Camel所做的。

延迟消息可以用常量表示

from("seda:b").delay(1000).to("mock:result");

或变量,例如消息头值

from("seda:a").delay().header("MyDelay").to("mock:result");
于 2013-03-07T11:41:55.870 回答
1

1> 我建议使用消息队列,根据你的要求选择队列,但大多数情况下任何人都可以,我建议你选择基于协议JMS(活动mq)或AMQP(兔子mq)的队列并编写一个简单的包装它或使用 spring-> spring-jms 或 spring-amqp 提供的那些

2> 您可以编写队列消费者,以便他们通知您的系统有新消息到达,例如在兔子中,您可以实现 MessageListener 接口

 public class MyListener implements MessageListener {
     @Override
public void onMessage(Message message) {
     /* Handle the message */        

    }
}

3> 如果你像 <2> 那样使用异步消费者,你可以摆脱所有轮询和 cron 作业

4> 取决于您的要求 -> 如果您有数百万个事件/消息通过您的队列,那么在集中式服务器上运行队列中间件是有意义的。

5> 如果资源消耗不是问题,那么让您的消费者/订阅者一直运行是最简单的方法。如果这些消费者是分布式的,那么您可以使用诸如 zookeeper 之类的服务来编排它们

6> 可扩展性 -> 大多数队列系统都提供了简单的消息分发,因此只要您的消费者是无状态的,那么只需添加新的消费者和一些配置就可以进行扩展。

于 2013-03-15T05:59:51.677 回答
0

实施后,我觉得回答我自己的问题对将来会来访问 StackOverflow 的人有好处。

最后,我选择了 Redis。它真的很快,而且可扩展。而且我非常喜欢它的灵活性:它比消息队列灵活得多。我是否断言 Redis 在 MQ 方面比现有的各种 MQ 更好?好吧,在我的具体情况下,我相信是这样。关键是:如果某些东西不是开箱即用的,您可以构建它(通常使用 MULTI - 但您甚至可以使用 LUA 进行更高级的定制!)。

例如,我遵循这个很好的答案来实现一个“持久的”、可恢复的 pub/sub(即允许客户端死亡并重新连接而不会丢失消息的 pub/sub)。

这对我的可扩展性和“可靠性”要求都有帮助:我决定让管道中的每个部分保持独立(现在是一个守护进程),但添加一个监视器来检查 Redis 上的列表/队列;如果某些东西没有被消费(或消费太慢),监视器就会产生一个新的消费者。我也在想真正的“弹性”,增加消费者在无事可做时自杀的能力。

另一个例子:计划活动的执行。我目前正在遵循这种似乎很流行的方法。但我很想尝试keyspace 通知,看看是否将过期密钥和通知结合起来是一种更好的方法。

最后,作为一个访问 Redis 的库,我选择了 Jedis:它很受欢迎,受支持,并且提供了一个很好的接口来实现 pub/sub 作为监听器。这不是 Scala 的最佳方法(惯用),但效果很好。

于 2013-07-25T13:37:44.680 回答