RabbitMQ Consumer获取消息的两种方式(poll,subscribe)解析

rabbitMQ中consumer通过建立到queue的连接,创建channel对象,通过channel通道获取message,
Consumer可以声明式的以API轮询poll的方式主动从queue的获取消息,也可以通过订阅的方式被动的从Queue中消费消息,
最近翻阅了基于java的客户端的相关源码,简单做个分析。
编程模型伪代码如下:
ConnectionFactory factory = new ConnectionFactory();
Connection conn = factory.newConnection();
Channel channel=conn.createChannel();
创建Connection需要指定MQ的物理地址和端口,是socket tcp物理连接,而channel是一个逻辑的概念,支持在tcp连接上创建多个MQ channel
以下是基于channel上的两种消费方式。

1、Subscribe订阅方式
boolean autoAck = false;
channel.basicConsume(queueName, autoAck, "myConsumerTag",
     new DefaultConsumer(channel) {
         @Override
         public void handleDelivery(String consumerTag,
                                    Envelope envelope,
                                    AMQP.BasicProperties properties,
                                    byte[] body)
             throws IOException
         {
             String routingKey = envelope.getRoutingKey();
             String contentType = properties.contentType;
             long deliveryTag = envelope.getDeliveryTag();
             // (process the message components here ...)
             channel.basicAck(deliveryTag, false);
         }
     });

订阅方式其实是向queue注册consumer,通过rpc向queue server发送注册consumer的消息,rabbitMQ Server在收到消息后,根据消息的内容类型判断这是一个订阅消息,
这样当MQ 中queue有消息时,会自动把消息通过该socket(长连接)通道发送出去。
参见ChannelN中的方法
    public String basicConsume(String queue, boolean autoAck, String consumerTag,
                               boolean noLocal, boolean exclusive, Map<String, Object> arguments,
                               final Consumer callback)
        throws IOException
    {
    ......
        rpc((Method)
            new Basic.Consume.Builder()
             .queue(queue)
             .consumerTag(consumerTag)
             .noLocal(noLocal)
             .noAck(autoAck)
             .exclusive(exclusive)
             .arguments(arguments)
            .build(),
            k);

        try {
            return k.getReply();
        } catch(ShutdownSignalException ex) {
            throw wrap(ex);
        }
    }

Consumer接收消息的过程:
创建Connection后,会启动MainLoop后台线程,循环从socket(FrameHandler)中获取数据包(Frame),调用channel.handleFrame(Frame frame)处理消息,
    public void handleFrame(Frame frame) throws IOException {
        AMQCommand command = _command;
        if (command.handleFrame(frame)) { // 对消息进行协议assemble
            _command = new AMQCommand(); // prepare for the next one
            handleCompleteInboundCommand(command);//对消息消费处理
        }
    }
ChannelN.handleCompleteInboundCommand
       ---ChannelN.processAsync
           ----dispatcher.handleDelivery
                 ---QueueingConsumer.handleDelivery
                     ---this._queue.add(new Delivery(envelope, properties, body));//消息最终放到队列中
每个Consumer都有一个BlockQueue,用于缓存从socket中获取的消息。
接下来,Consumer对象就可以调用api来从客户端缓存的_queue中依次获取消息,进行消费,参见QueueingConsumer.nextDelivery()

对于这种长连接的方式,没看到心跳功能,以防止长连接的因网络等原因连接失效


2、poll API方式
ChannelN:
GetResponse basicGet(String queue, boolean autoAck)
这种方式比较简单,直接通过RPC从MQ Server端获取队列中的消息


转载 :http://blog.youkuaiyun.com/yangbutao/article/details/10395599

### 实现 Kafka 消息队列数据推送至 RabbitMQ 的方法 要将 Kafka 消息队列中的数据推送到 RabbitMQ,可以通过编写一个中间服务来消费 Kafka 中的消息,然后将其发布到 RabbitMQ 中。这种方案的核心是将 Kafka 的消费者RabbitMQ 的生产者整合到一个服务中,实现消息的桥接。 #### 1. Kafka 消费端实现 首先,需要编写一个 Kafka 消费者来订阅 Kafka 中的消息。可以通过 Kafka 提供的消费者 API 实现,例如使用 `KafkaConsumer` 来拉取消息[^2]。以下是一个 Kafka 消费者的示例代码: ```java Properties props = new Properties(); props.put("bootstrap.servers", "localhost:9092"); props.put("group.id", "test"); props.put("enable.auto.commit", "true"); props.put("auto.commit.interval.ms", "1000"); props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("my-topic")); while (true) { ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100)); for (ConsumerRecord<String, String> record : records) System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value()); } ``` #### 2. RabbitMQ 生产端实现 在消费 Kafka 消息后,需要将消息发布到 RabbitMQ。可以通过 RabbitMQ 提供的 Java 客户端实现,例如使用 `Channel` 的 `basicPublish` 方法。以下是一个 RabbitMQ 生产者的示例代码: ```java ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); channel.queueDeclare("my-queue", false, false, false, null); String message = "Hello World!"; channel.basicPublish("", "my-queue", null, message.getBytes()); System.out.println(" [x] Sent '" + message + "'"); ``` #### 3. 桥接服务整合 将 Kafka 消费者RabbitMQ 生产者整合到一个服务中,可以实现消息的桥接。具体步骤如下: - 在 Kafka 消费者的回调函数中,获取消息后,调用 RabbitMQ 的生产端代码将消息发布到 RabbitMQ。 - 需要注意消息的格式转换,确保 Kafka 消息的格式与 RabbitMQ 消息的格式兼容。 - 可以通过线程池或异步处理提高性能,确保 Kafka 消费和 RabbitMQ 生产的效率。 #### 4. 消息确认与可靠性 Kafka 和 RabbitMQ 都支持消息的确认机制,但在桥接服务中需要特别注意消息的可靠性传递。例如: - 在 Kafka 中,可以通过手动提交偏移量确保消息被成功消费。 - 在 RabbitMQ 中,可以通过 `confirm` 机制确保消息被成功发布。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值