我有一个由第三方发布的JMS队列。 我想在不同的机器上设置多个消费者,只有一个特定机器的消费者,确认该队列上的消息。简而言之,如果特定计算机的使用者没有收到消息,则不应从队列中删除该消息。 这可以实现吗?
答案 0 :(得分:1)
好的,您可能有理由进行此设置,并且很容易实现。
我会选择本地会话交易。根据某些标准提交或回滚事务相当容易,例如哪个服务器正在使用该消息。如果回滚,则消息将再次在队列中首先结束。
示例代码可能如下所示:
public class MyConsumer implements MessageListener{
Session sess;
public void init(Connection conn, Destination dest){
// connection and destination from JNDI, or some other method.
sess = conn.createSession(true, Session.AUTO_ACKNOWLEDGE);
MessageConsumer cons = sess.createConsumer(dest);
cons.setMessageListener(this);
conn.start();
}
@Override
public void onMessage(Message msg) {
// Do whatever with message
if(isThisTheSpecialServer()){
sess.commit();
}else{
sess.rollback();
}
}
private boolean isThisTheSpecialServer(){
// figure out if this server should delete messages or not
}
}
如果您在使用JTA的Java EE容器中执行此操作并且您正在使用UserTransactions,则可以只调用UserTransaction.setRollBack(); 或者,如果您正在使用声明性事务,则只需抛出一个运行时异常即可使事务失败,并在您阅读完消息并完成操作后将消息回滚到队列中。请注意,使用此方法也会回滚数据库更改(如果您使用的是JTA而非本地JMS事务)。
更新:
你应该使用交易而不是确认来做到这一点。
此主题的摘要(适用于ActiveMQ,但通常为JMS编写)可在此处找到。 http://activemq.apache.org/should-i-use-transactions.html
我不知道这种行为是否与所有JMS实现一致,但对于ActiveMQ,如果您尝试使用Session.CLIENT_ACKNOWLEDGEMENT的非事务会话,那么它将不会像您期望的那样真正表现。已读取但未确认的消息仍在队列中,但在连接断开连接到第一个使用者(即connection.close(),崩溃或崩溃之前,将不会“释放”并传递给其他JMS使用者相似)。
使用本地事务,您可以通过session.commit()和session.rollback()显式地控制它。我认为不使用交易没有实际意义。致谢就是保证交付。
答案 1 :(得分:0)
另一种看待这种情况的方法是转发队列。您可以通过执行以下操作将其应用于您的设计:
通过执行此操作,您可以确保每个侦听器都能看到每条消息,每个事务都按预期工作,并且您不会对消息的发送方式做出任何假设(例如,如果发布方正在执行该操作{{ 1}}?)