Java异步处理

时间:2012-02-03 06:40:27

标签: java architecture asynchronous hazelcast

我目前正在开发一个使用异步处理的系统。使用队列完成信息传输。因此,一个进程将把信息放入队列(并终止),另一个进程将获取并处理它。我的实现让我面临许多挑战,我对每个人的方法都是对这些问题感兴趣(在架构和库方面)。

让我画一幅画。假设你有三个过程:

Process A -----> Process B
                      |
Process C <-----------|

所以进程A 将消息放入队列并结束,进程B 获取消息,处理消息并将其放入“返回”队列。 流程C 会收集邮件并对其进行处理。

  1. 如何处理进程B 没有侦听或处理队列中的消息?是否有一些JMS类型方法阻止Producer在Consumer不活动时提交消息?所以流程A 会提交,但会抛出异常。
  2. 让我们说进程C 必须在X分钟内得到回复,但是进程B 已停止(出于任何原因),是否有某种机制强制执行超时在队列上?因此,保证在X分钟内回复,这将启动流程C
  3. 是否可以使用某种死信队列来处理所有这些问题?我是否可以通过计时器和检查手动完成所有操作。我已经提到了JMS,但我对任何事情持开放态度,事实上我正在使用Hazelcast作为队列。

    请注意,就可用的Java技术和方法而言,这更像是一个架构问题,我觉得这是一个恰当的问题。

    任何建议都将不胜感激。

    由于

6 个答案:

答案 0 :(得分:2)

在我看来,你问的问题类型是“气味”,排队和异步处理可能不是适合你情况的最佳工具。

1)这违背了队列的目的。听起来你需要一个同步的请求 - 响应过程。

2)一般来说,进程C没有得到答复。它从队列中获取消息。如果队列中有消息且进程C已准备就绪,那么它将获得它。例如,进程C可以在消息获得后判定该消息是陈旧的。

答案 1 :(得分:2)

我认为你的第一个问题已经被其他海报充分回答了。

关于第二个问题,根据应用程序使用的消息传递引擎,您可能会尝试执行的操作。我知道这适用于IBM MQ。我已经看到这是使用 WebSphere MQ Classes for Java 而不是JMS完成的。它的工作方式是当进程A 将消息放入队列时,它指定等待响应消息的时间。如果进程A 未能在指定时间内收到响应消息,系统将引发相应的异常。

我认为JMS中没有一种标准的方式可以按您希望的方式处理请求/响应超时,因此您可能必须使用特定于平台的类,例如WebSphere MQ Classes for Java。

答案 2 :(得分:2)

恕我直言,最简单的解决方案是使用ExecutorService或基于执行程序服务的解决方案。这支持工作队列,计划任务(用于超时)。

它也可以在一个过程中工作。 (我相信Hazelcast支持分布式ExecutorService)

答案 3 :(得分:1)

嗯,排队的一点是保持相当孤立。

如果您没有遇到任何特定技术,您可以使用数据库作为队列。

但首先,确保两个进程协调的简单机制是使用套接字。如果可行,只需让进程B在一个众所周知的端口上创建一个打开的套接字侦听器,进程A将连接到该套接字并监视它。如果进程B消失了,进程A可以告诉它,因为它们的套接字被关闭了,它可以将它用作进程B问题的警告。

对于B - &gt; C问题,有一个db表:

create table queue (
    id integer,
    payload varchar(100), // or whatever you can use to indicate a payload
    status varchar(1),
    updated timestamp
)

然后,进程A将其条目放入队列,其中包含当前时间和状态“B”。 B,听队列:

select * from queue where status = 'B' order by updated

完成B后,它会更新队列以将状态设置为“C”。

与此同时,“C”正在使用以下方式轮询数据库:

select * from queue where status = 'C' 
    or (status = 'B' and updated < (now - threshold) order by updated 

(阈值很长,你想要在队列上腐烂)。

最后,C将队列行更新为“D”表示已完成,或删除它或任何您喜欢的内容。

黑暗的一面是这里有一些竞争条件,当B刚刚开始时,C可能会尝试抓住一个条目。你可以通过严格的隔离级别和一些锁定来完成它。简单的事情:

select * from queue where status = 'C' 
    or (status = 'B' and updated < (now - threshold) order by updated 
FOR UPDATE

也可以使用FOR UPDATE进行B选择。这样,无论谁赢得精选比赛,都会获得该排的独家锁定。

这将使您在实际功能方面走得更远。

答案 4 :(得分:1)

您期望使用异步(消息传递)设置进行同步处理的语义,这是不可能的。我一直在使用WebSphere MQ,通常当消费者死亡时,消息将永久保存在队列中(除非您设置了到期日期)。队列到达深度后,后续消息将移至死信队列。

答案 5 :(得分:1)

我使用类似的方法为视频转码作业创建排队和处理系统。基本上它的工作方式是:

  1. Process A向“{1}}发送”日程安排“消息,将该作业添加到”等待“队列中。
  2. Arbiter QProcess B请求下一个作业,该作业将删除其“等待”队列中的下一个项目(需要一些自定义调度逻辑,以确保单个用户无法填充转码请求和阻止其他用户转码视频)并将其插入“处理”设置,然后将作业返回Arbiter Q。当作业进入“处理”集时,作业将加上时间戳。
  3. Process B完成作业并向Process B发送“完整”消息,从“处理”集中删除作业,然后修改某个状态,以便Arbiter Q知道作业完成。
  4. Process C会定期检查其“处理”集中的作业,并超时任何已经运行了非常长时间的作业。如果需要,Arbiter Q可以自由尝试再次排队同一个作业。
  5. 这是使用JMX实现的(JMS会更合适,但我离题了)。 Process A只是响应用户启动的转码请求的servlet线程。 Process A是一个MBean单例(在服务器集群中的所有节点上持久化/复制),它们收到“schedule”和“complete”消息。其内部管理的“队列”只是Arbiter Q个实例,当作业完成时,它修改了应用程序数据库中的值,以引用转码后的视频文件的URL。 List是转码线程。它的工作只是要求一份工作,对其进行转码,然后在完成时报告。一遍又一遍,直到时间结束。 Process B是另一个用户/ servlet线程。它会看到URL可用,并向用户显示下载链接。

    在这种情况下,如果Process C死亡,则作业将永远处于“等待”队列中。然而,在实践中,从未发生过。如果你的Process B没有运行/正在做它应该做的事情那么我认为这表明你{/ 1}}的部署/配置/实现中的问题比你整体方法中的问题更多。< / p>