RocketMQ——生产者和消费者

RocketMQ——生产者和消费者


RocketMQ简介

RocketMQ共有四个角色,分别是Producer、Consumer、Broker、NameServer,他们分别对应的作用如下:

  • Producer:消息生产者,负责消息的生产发送
  • Consumer:消息消费者,负责消息接收和使用
  • Broker:负责消息的传输和暂存
  • NameServer:负责协调整个消息队列,维护配置信息和状态信息

RocketMQ架构

RocketMQ生产者

消息生产者向消息队列写入数据,在不同的业务场景中可能会有不同的需求,下面介绍消息生产者

public class producer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("group2");//设置group名
        producer.setNamesrvAddr("192.168.108.128:9876");//设置Nameserver地址
        producer.setVipChannelEnabled(false);
        producer.start();//开启生产者
        Message msg = new Message("TopicTest2", 
        							"TagA",
        		("MQTest").getBytes(RemotingHelper.DEFAULT_CHARSET));
        msg.setDelayTimeLevel(3);//设置延迟发送消息
        SendResult result = producer.send(msg);//发送消息
        System.out.println(result);
        producer.shutdown();
    }
}

在这里使用了默认消息生产类DefaultMQProducer,并且在创建好消息后通过调用setDelayTimeLevel(int level)方法设置延迟时间。

RocketMQ消费者

消费者可以分为两类

  • DefaultMQPushConsumer:系统控制读取操作
  • DefaultMQPullConsumer:使用者自主控制读取操作
    但是这两个的本质都是通过Consumer轮询Broker然后再拉取消息,即pull模式

DefaultMQPushConsumer

DefaultMQPushConsumer通过用户设置好的参数等,系统会自动根据用户设置参数进行消息处理,自动保存Offset、做负载均衡等。

public class consumer {
    public static void main(String[] args) throws Exception {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group2");//设置group名
        consumer.setNamesrvAddr("192.168.108.128:9876");//设置Nameserver地址
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);//设置读取位置,从最小的offset开始
        consumer.subscribe("TopicTest2", "*");//生命接受的Topic名以及过滤tags
        consumer.setMessageModel(MessageModel.BROADCASTING);
        consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> {
            System.out.println(Thread.currentThread().getName() + " Receive Message: " + list);
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        });
        System.out.println("consumer start...");
        consumer.start();
    }
}

在程序中创建好了consumer对象之后,需要对Group、MessageModel、NameServer、Topic进行设置:

  • Group主要将多个Consumer组织到一起,提高并发能力
  • RocketMQ支持两种MessageModel消息模式:Clustering和BroadCasting。Clustering模式下,在同一个group中的消费者只会消费消息的一部分,即group内所有消费者的消费消息的总和是Topic内容整体。Broadcasting即广播模式,消息会同时发布到group内的每一个consumer。
  • NameServer需要配置ip和端口号,多个NameServer用分号(;)隔开
  • Topic是消息队列的标识,表明消费者需要消费哪些消息,并且可以通过消息的tags来进行过滤,如:consumer.subscribe(“TopicTest2”, “tag1||tag2||tag3”);此处使用 * 表示消费topic中的所有消息

在前面说过,DefaultMQPushConsumer虽然叫Push,其原理依然是Pull模式,怎么实现用Pull达到Push效果的呢?

首先我们来看看Push和Pull两种方式的优缺点:

  • Push方式是在服务端接收到消息后立刻将消息推送到客户端去,这样的方式实时性高,但是如果在消息队列中使用这种方式首先是加大服务端的压力,一收到消息就立刻推送到客户端,会降低服务端的部分性能。其次,如果在推送过程中如果客户端发生阻塞,导致客户端不能及时处理消息,消息会在客户端堆积,从而造成各种潜在问题。
    在这里插入图片描述
    在这里插入图片描述
  • Pull方式是客户端向服务端发起请求拉取数据,这种方式的优点是由客户端控制消息接收,可以使客户端处理完消息后再去服务端拉取下一条消息,防止消息在客户端堆积。这样的方式的缺点是需要对客户端拉取消息时间间隔进行把握,间隔过长实时性较低,间隔过短同样会造成消息堆积。
    在这里插入图片描述
    DefaultMQPushConsumer的主要功能都在类DefaultMQPushConsumerImpl中,这个类实现MQConsumerInner接口,该类中有个很长的方法叫public void pullMessage(final PullRequest pullRequest) ,该方法是处理消息的主要方法,并且在DefaultMQPushConsumer中又有非常多的Pull方法。

那么RocketMQ是怎样做到用Pull的方式实现Push的效果呢?其实PushConsumer是通过长轮询的方式来达到Push的效果。Broker将Consumer发送过来的拉取请求保持住,然后Consumer循环查看队列中是否有消息,如果没有消息则等待5秒,然后再去查看队列是否有消息,如果有消息则拉取,如果没有消息,就继续等待。当超过设置的BrokerSuspendMaxTimeMillis时(默认时15s,在默认情况下Consumer会等待3次),返回空结果。此时消息的控制权在Consumer上,大大减少了Consumer客户端的消息处理压力,但是在保持链接时会暂用一定的资源。

DefaultMQPullConsumer

下面代码是org.apache.rocketmq.example.simple中的示例代码,该代码演示了从消息队列中使用Pull模式拉取消息的步骤

public class PullConsumer {
    private static final Map<MessageQueue, Long> OFFSE_TABLE = new HashMap<MessageQueue, Long>();
 
    public static void main(String[] args) throws MQClientException {
        DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("please_rename_unique_group_name_5");
         consumer.start();
 		//根据Topic名获取队列
        Set<MessageQueue> mqs = consumer.fetchSubscribeMessageQueues("TopicTest1");
        for (MessageQueue mq : mqs) {
            //获取消息的offset
            long Offset = Consumer.fetchConsumeOffset(mq, true);
            System.out.printf("Consume from the queue: %s%n", mq);
            SINGLE_MQ:
            while (true) {
                try {
                    PullResult pullResult =
                        consumer.pullBlockIfNotFound(mq, null, getMessageQueueOffset(mq), 32);
                    System.out.printf("%s%n", pullResult);
                    putMessageQueueOffset(mq, pullResult.getNextBeginOffset());
                    //根据不同的效应进行处理
                    switch (pullResult.getPullStatus()) {
                        case FOUND:
                            break;
                        case NO_MATCHED_MSG:
                            break;
                        case NO_NEW_MSG:
                            break SINGLE_MQ;
                        case OFFSET_ILLEGAL:
                            break;
                        default:
                            break;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
 
        consumer.shutdown();
    }
 
    private static long getMessageQueueOffset(MessageQueue mq) {
        Long offset = OFFSE_TABLE.get(mq);
        if (offset != null)
            return offset;
 
        return 0;
    }
 
    private static void putMessageQueueOffset(MessageQueue mq, long offset) {
        OFFSE_TABLE.put(mq, offset);
    }
 }

使用Pull模式拉取消息额外做了三件事

  1. 获取MessageQueue并遍历。因为一个Topic可能包含多个MessageQueue,因此需要对MessageQueue进行遍历才能获取;
  2. 维护Offsetstore。从MessageQueue拉去消息时,需要传入Offset值,随着MessageQueue的增长,需要用户存储Offset;
  3. 根据不同响应状态进行处理。拉去请求会返回FOUND(获取消息)、NO_MATCHED_MSG(没有合适消息)、NO_NEW_MSG(没有新消息)、OFFSET_ILLEGAL(不合法的Offset)
  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值