您能以编程方式更改Java嵌入式代理中队列的“死信”处理吗?

时间:2014-09-23 19:27:38

标签: java multithreading jms apache-camel activemq

背景

在高级别,我有一个Java应用程序,其中某些事件应触发对当前用户采取的某个操作。但是,事件可能非常频繁,并且操作始终相同。因此,当第一个事件发生时,我想在不久的将来(例如5分钟)安排行动。在该时间窗口期间,后续事件不应采取任何操作,因为应用程序发现已经安排了操作。一旦计划的操作执行,我们将返回步骤1,下一个事件将再次开始循环。

我的想法是通过在应用程序本身内嵌入内存中的ActiveMQ实例来实现这种过滤和限制机制(我不关心队列持久性)。

我认为JMS 2.0支持延迟传送的概念,延迟消息位于“暂存队列”中,直到传送到真实目的地为止。但是,我也相信ActiveMQ还不支持JMS 2.0规范......所以我正在考虑使用生存时间(TTL)值和死信队列(DLQ)处理来模仿相同的行为。

基本上,我的消息生成器代码会将消息放在虚拟登台队列 上,消费者从中没有任何消息 。消息将以5分钟的TTL值放置,并在到期时ActiveMQ将它们转储到DLQ中。 那是 我的消息使用者实际消费消息的队列。

问题

我认为我不想实际使用“默认”DLQ,因为我不知道ActiveMQ可能在那里转储哪些与我的应用程序代码完全无关的内部事物。所以我认为我的虚拟登台队列最好有自己的自定义DLQ。我只看过one page of ActiveMQ documentation which discusses DLQ config,它只针对独立的ActiveMQ安装(不是嵌入在应用程序中的内存代理)的XML配置文件。

是否可以在运行时以编程方式为嵌入式ActiveMQ实例中的队列配置自定义DLQ?

如果您认为我走错了路,我也有兴趣听取其他建议。我对JMS比AMQP更熟悉,所以我不知道Qpid或其他一些Java嵌入式AMQP代理是否更容易。无论Apache Camel实际上是什么(!),我相信它应该在这种情况下表现出色,但是这种学习曲线对于这个用例来说可能是一个严重的过度杀伤。

3 个答案:

答案 0 :(得分:4)

虽然你担心Camel对于这个用例可能会严重过度,但我认为ActiveMQ对于你所描述的用例来说已经严重过度了。

您希望在事件发生5分钟后安排事情发生,并且只消耗第一个事件而忽略第一个事件之间的所有事件以及5分钟之后的所有事件,对吗?为什么不从现在开始通过ScheduledExecutorService或您最喜欢的调度机制安排处理方法5分钟,并将事件保存在HashMap<User, Event>成员变量中。如果在处理方法触发之前有更多事件进入该用户,您将看到已经存储了一个事件而没有存储新事件,因此您将忽略除第一个之外的所有事件。在处理方法结束时,请从HashMap中删除此用户的活动,然后将存储和安排下一个活动。

运行ActiveMQ只是为了获得这种行为似乎比你需要的更多。如果没有,你能解释一下原因吗?

编辑:

如果您沿着这条路走下去,请不要使用消息TTL来过期消息;让(唯一的)消费者将它们读入内存并使用上述内存解决方案仅每5分钟处理一次(最多)一批。要么具有带有消息选择器的单个队列,要么使用动态队列,每个用户一个。你不需要DLQ来实现延迟,即使你可以做到这一点,它也不会给你批处理所有功能,所以你每5分钟只运行一次。即使你弄清楚如何,这也不是你想要失败的道路。

答案 1 :(得分:1)

一个简单的解决方案是跟踪并发结构中的待处理操作,并使用ScheduledExecutorService执行它们:

private static final Object RUNNING = new Object();
private final ConcurrentMap<UserId, Object> pendingActions = 
    new ConcurrentHashMap<>();
private ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);


public void takeAction(final UserId id) {
    Object running = pendingActions.putIfAbsent(id, RUNNING);  // atomic
    if(running == null) {                // no pending action for this user
        ses.schedule(new Runnable() {
            @Override
            public void run() {
                doWork();
                pendingActions.remove(id);
            }
        }, 5, TimeUnit.MINUTES);
    }
}

答案 2 :(得分:1)

使用Camel可以使用参数completionInterval的{​​{3}}组件轻松实现,因此每隔五分钟就可以检查列表聚合消息是否为空,如果不是向负责用户操作的路由发送消息并清空列表。您确实需要维护整个交换列表,只需要维护状态(计划用户行为与否)。