在 JMS(Java Message Service)里,事务与确认机制是保证消息可靠传递和处理的关键

在 JMS(Java Message Service)里,事务与确认机制是保证消息可靠传递和处理的关键。下面详细介绍它们在 JMS 中的具体实现方式。

事务机制

事务性会话

JMS 借助事务性会话达成事务操作。创建会话时,可将其设定为事务性会话,即把 createSession 方法的第一个参数设为 true。在事务性会话中,消息的发送和接收操作会被封装在一个事务里,只有当事务提交时,这些操作才会生效;若事务回滚,操作将被撤销。

生产者事务示例
import javax.jms.*;
import org.apache.activemq.ActiveMQConnectionFactory;

public class JMSProducerTransactionExample {
    public static void main(String[] args) {
        String brokerUrl = "tcp://localhost:61616";
        ConnectionFactory factory = new ActiveMQConnectionFactory(brokerUrl);
        try (Connection connection = factory.createConnection();
             // 创建事务性会话
             Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
             Destination destination = session.createQueue("testQueue");
             MessageProducer producer = session.createProducer(destination)) {
            connection.start();
            // 创建并发送多条消息
            for (int i = 0; i < 5; i++) {
                TextMessage message = session.createTextMessage("Message " + i);
                producer.send(message);
            }
            // 提交事务
            session.commit();
            System.out.println("Messages sent successfully.");
        } catch (JMSException e) {
            try {
                // 若发生异常,回滚事务
                session.rollback();
                System.out.println("Transaction rolled back.");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }
    }
}

上述代码创建了一个事务性会话,生产者在该会话中发送了 5 条消息。若整个过程没有异常,就会提交事务,消息会被真正发送到队列;若出现异常,则回滚事务,消息不会被发送。

消费者事务示例
import javax.jms.*;
import org.apache.activemq.ActiveMQConnectionFactory;

public class JMSConsumerTransactionExample {
    public static void main(String[] args) {
        String brokerUrl = "tcp://localhost:61616";
        ConnectionFactory factory = new ActiveMQConnectionFactory(brokerUrl);
        try (Connection connection = factory.createConnection();
             // 创建事务性会话
             Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
             Destination destination = session.createQueue("testQueue");
             MessageConsumer consumer = session.createConsumer(destination)) {
            connection.start();
            // 接收消息
            Message message = consumer.receive();
            if (message instanceof TextMessage) {
                TextMessage textMessage = (TextMessage) message;
                System.out.println("Received: " + textMessage.getText());
            }
            // 处理消息(此处可添加业务逻辑)
            // 提交事务
            session.commit();
            System.out.println("Message processed and transaction committed.");
        } catch (JMSException e) {
            try {
                // 若发生异常,回滚事务
                session.rollback();
                System.out.println("Transaction rolled back.");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }
    }
}

此代码创建了一个事务性会话,消费者在该会话中接收消息。若消息处理过程没有异常,就会提交事务,消息会被标记为已消费;若出现异常,则回滚事务,消息不会被标记为已消费,后续可再次被消费。

确认机制

自动确认(Session.AUTO_ACKNOWLEDGE

在自动确认模式下,消费者接收到消息后,JMS 会自动把消息标记为已消费。这是最简单的确认模式,但存在一定风险,若消费者在处理消息时出现异常,消息已被标记为已消费,就会导致消息丢失。

import javax.jms.*;
import org.apache.activemq.ActiveMQConnectionFactory;

public class JMSAutoAcknowledgeExample {
    public static void main(String[] args) {
        String brokerUrl = "tcp://localhost:61616";
        ConnectionFactory factory = new ActiveMQConnectionFactory(brokerUrl);
        try (Connection connection = factory.createConnection();
             // 创建非事务性会话,使用自动确认模式
             Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
             Destination destination = session.createQueue("testQueue");
             MessageConsumer consumer = session.createConsumer(destination)) {
            connection.start();
            Message message = consumer.receive();
            if (message instanceof TextMessage) {
                TextMessage textMessage = (TextMessage) message;
                System.out.println("Received: " + textMessage.getText());
            }
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}
手动确认(Session.CLIENT_ACKNOWLEDGE

手动确认模式下,消费者需要显式调用 acknowledge() 方法来确认消息已消费。这样消费者就能在处理完消息后再确认,避免消息丢失。

import javax.jms.*;
import org.apache.activemq.ActiveMQConnectionFactory;

public class JMSClientAcknowledgeExample {
    public static void main(String[] args) {
        String brokerUrl = "tcp://localhost:61616";
        ConnectionFactory factory = new ActiveMQConnectionFactory(brokerUrl);
        try (Connection connection = factory.createConnection();
             // 创建非事务性会话,使用手动确认模式
             Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
             Destination destination = session.createQueue("testQueue");
             MessageConsumer consumer = session.createConsumer(destination)) {
            connection.start();
            Message message = consumer.receive();
            if (message instanceof TextMessage) {
                TextMessage textMessage = (TextMessage) message;
                System.out.println("Received: " + textMessage.getText());
                // 手动确认消息
                message.acknowledge();
            }
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}
事务确认(Session.SESSION_TRANSACTED

事务确认模式与事务性会话结合使用。在事务性会话中,消息的确认操作会在事务提交时进行。若事务回滚,消息不会被确认,后续可再次被消费。前面的生产者和消费者事务示例中就采用了这种确认模式。

预取确认(Session.DUPS_OK_ACKNOWLEDGE

预取确认模式允许消费者批量确认消息,以提高性能。在这种模式下,消费者可能会收到重复的消息,因此需要在业务逻辑中处理消息的重复问题。

import javax.jms.*;
import org.apache.activemq.ActiveMQConnectionFactory;

public class JMSDupsOkAcknowledgeExample {
    public static void main(String[] args) {
        String brokerUrl = "tcp://localhost:61616";
        ConnectionFactory factory = new ActiveMQConnectionFactory(brokerUrl);
        try (Connection connection = factory.createConnection();
             // 创建非事务性会话,使用预取确认模式
             Session session = connection.createSession(false, Session.DUPS_OK_ACKNOWLEDGE);
             Destination destination = session.createQueue("testQueue");
             MessageConsumer consumer = session.createConsumer(destination)) {
            connection.start();
            Message message = consumer.receive();
            if (message instanceof TextMessage) {
                TextMessage textMessage = (TextMessage) message;
                System.out.println("Received: " + textMessage.getText());
            }
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

综上所述,JMS 的事务与确认机制提供了多种方式来保证消息的可靠传递和处理,开发者可根据具体的业务需求选择合适的机制。
JMS(Java Message Service)定义了两种主要的消息模型:点对点(P2P)模型发布/订阅(Pub/Sub)模型。这两种模型分别对应 队列(Queue)主题(Topic),它们在消息传递方式、消息接收者数量、消息持久性等方面存在显著区别。以下是队列和主题的详细对比:

1. 消息传递方式

  • 队列(Queue)

    • 点对点模型:生产者将消息发送到队列,消费者从队列中接收消息。
    • 一对一:每个消息只能被一个消费者接收,确保消息不会重复处理。
    • 适用场景:适用于需要确保消息只被处理一次的场景,如订单处理、任务分配等。
  • 主题(Topic)

    • 发布/订阅模型:生产者将消息发布到主题,多个订阅者可以接收同一消息。
    • 一对多:每个消息可以被多个订阅者接收,适用于广播消息的场景。
    • 适用场景:适用于需要将消息广播给多个消费者的场景,如日志监控、事件通知等。

2. 消息接收者数量

  • 队列

    • 每个消息只能被一个消费者接收。
    • 如果有多个消费者订阅同一个队列,消息会被分发给其中一个消费者,其他消费者不会接收到该消息。
  • 主题

    • 每个消息可以被多个订阅者接收。
    • 如果有多个订阅者订阅同一个主题,每个订阅者都会接收到该消息。

3. 消息持久性

  • 队列

    • 消息的持久性由队列的持久化属性决定。
    • 生产者可以设置消息的持久性属性(deliveryMode),确保消息在系统故障时不会丢失。
    • 消费者处理完消息后发送ACK确认,队列中的消息才会被删除。
  • 主题

    • 消息的持久性由主题的持久化属性决定。
    • 持久化订阅(Durable Subscription):订阅者即使在离线时,也能接收到消息。
    • 非持久化订阅(Non-Durable Subscription):订阅者必须在线才能接收到消息,否则消息会被丢弃。

4. 消息存储与生命周期

  • 队列

    • 消息存储在队列中,直到被消费者接收并确认。
    • 消息的生命周期由队列管理,消息在被消费后从队列中删除。
  • 主题

    • 消息存储在主题中,直到被所有订阅者接收。
    • 消息的生命周期由主题管理,消息在所有订阅者接收后才会被删除。
    • 对于持久化订阅,消息会一直存储在主题中,直到订阅者上线并接收。

5. 消费者行为

  • 队列

    • 消费者通过MessageConsumer从队列中接收消息。
    • 消费者可以采用同步(receive())或异步(MessageListener)方式接收消息。
    • 消费者处理完消息后发送ACK确认,队列中的消息才会被删除。
  • 主题

    • 消费者通过MessageConsumer从主题中接收消息。
    • 消费者可以采用同步(receive())或异步(MessageListener)方式接收消息。
    • 持久化订阅的消费者即使离线,也能在上线后接收到消息。

6. 适用场景

  • 队列

    • 任务分配:将任务放入队列,由多个消费者并行处理,确保任务只被处理一次。
    • 订单处理:确保订单消息只被处理一次,避免重复处理。
    • 日志处理:将应用产生的日志发送到队列,由专门的日志处理服务进行分析和存储。
  • 主题

    • 事件通知:将事件消息发布到主题,多个订阅者可以接收并处理。
    • 监控上报:将服务器的监控数据发布到主题,多个监控系统可以接收并处理。
    • 广播消息:将消息广播给多个消费者,确保每个消费者都能接收到。

7. 性能与资源占用

  • 队列

    • 通常用于处理单个任务,资源占用相对较低。
    • 适合处理高并发任务,通过多个消费者分担负载。
  • 主题

    • 通常用于广播消息,资源占用相对较高。
    • 适合处理低频广播消息,避免消息积压。

总结

  • 队列:适用于需要确保消息只被处理一次的场景,如任务分配、订单处理等。
  • 主题:适用于需要将消息广播给多个消费者的场景,如事件通知、监控上报等。

通过理解队列和主题的区别,可以更好地选择适合业务需求的消息模型,实现高效、可靠的消息传递和处理。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bol5261

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值