MQ以异步方式处理,聚合和发布数据

时间:2013-03-07 11:21:05

标签: java notifications redis message-queue dataflow

在回答真正的问题之前有一些背景知识:

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

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

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

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

  1. 我不喜欢依赖"民意调查&#34 ;;考虑到以前模块的信息足以告诉"这是一种浪费。当他们需要的信息可用时,链中的其他模块可以继续。
  2. 这是"慢":链条上的模块有几天延迟,因为我们必须确保数据是由前面的模块到达和处理的。所以我们"停止"这些模块直到我们确定我们拥有所有数据。新增功能需要实时(不是很难,但是"尽快")计算某些指标。一个很好的例子就是在这里发生的事情,在SO上,带有徽章! :)我需要获得一些非常相似的东西。
  3. 为解决第二个问题,我将介绍" partial"或" incremental"计算:只要我有一组相关信息,我就会处理它。然后,当一些其他链接信息到达时,我计算差异并相应地更新数据,但我还需要通知其他(从属)模块。

    问题

    - 1)哪种方法最好? - 2)相关:这是"通知"的最佳方式。其他模块(在我的例子中是java可执行文件)相关数据是否可用?

    我可以看到三种方式:

    • 添加其他,"非数据"表格到DB,每个模块写入"嘿,我已经完成了这个并且它可以使用"。当cronjob启动另一个模块时,它会读取表格,确定他可以计算子集xxx,然后执行。等等。
    • 使用Message Queues,如ZeroMQ(或Apache Camel,如@mjn建议)而不是DB表
    • 使用像Redis这样的键值存储而不是数据库表
      

    编辑:我确信基于队列的方法是可行的方法,我添加了" table + polling"完整性的选择,但现在我明白这只是一种分心(显然,每个人都会回答"是的,使用队列,轮询是邪恶的" - 这是正确的!)。那么让我重新解释一下这个问题:   在像Redis这样的pub / sub的键值存储上使用MQ有什么优点/缺点?

    • 3)有没有任何解决方案可以帮我完全摆脱cronjobs?
      

    编辑:特别是,在可能的情况下,它意味着:在某些MQ和/或键值存储中是否有机制,允许我使用" time"?喜欢"在1天内发送"?坚持不懈,几乎一次"交货保证,显然

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

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

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

      

    编辑:骆驼对我来说似乎有点太重了。在资源和开发复杂性方面,我在这里寻找非常轻松的东西

3 个答案:

答案 0 :(得分:1)

队列任务描述听起来像基于" enterprise integration patterns"的系统。像Apache Camel那样。

delayed message可由常数

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

或变量,例如消息头值

from("seda:a").delay().header("MyDelay").to("mock:result");

答案 1 :(得分:1)

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

2 - ;您可以编写队列使用者,以便他们通知您的系统新消息到达,例如在Rabbit中您可以实现MessageListener接口

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

    }
}

3>如果您使用< 2>中的异步消费者你可以摆脱所有的民意调查和cron工作

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

5个如果资源消耗不是问题,那么让您的消费者/订阅者一直在运行是最简单的方法。如果这些消费者是分发的,那么你可以使用像zookeeper

这样的服务来编排它们

6个可伸缩性 - >大多数排队系统都可以轻松分发消息,因此只要您的消费者是无状态的,那么只需添加新的消费者和一些配置即可实现扩展。

答案 2 :(得分:0)

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

最后,我和Redis一起去了。 真的快速,可扩展。我非常喜欢它的灵活性:它比消息队列更灵活。我是否断言Redis在MQ上比那里的各种MQ更好?嗯,在我的具体情况下,我相信。关键是:如果没有提供开箱即用的东西,你可以构建它(通常,使用MULTI - 但你甚至可以使用LUA进行更高级的自定义!)。

例如,我跟着this good answer实现了一个“持久的”,可恢复的pub / sub(即允许客户端死掉并重新连接而不会丢失消息的pub / sub)。

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

另一个例子:执行预定的活动。我现在关注this approach,这似乎很受欢迎。但我很想尝试keyspace notifications,看看过期密钥和通知的组合是否是一种更好的方法。

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