RocketMq浅析

简介

RocketMQ是一款分布式、队列模型的消息中间件,是阿里巴巴集团自主研发的专业消息中间件,借鉴参考了JMS规范的MQ实现,更参考了优秀的开源消息中间件KAFKA,实现了业务消峰、分布式事务的优秀框架

为什么选择RocketMQ

在使用越来越多的队列和虚拟主题的情况下,ActiveMQ IO模块遇到了瓶颈。我们尽力通过节流,断路器或降级来解决此问题,但效果不佳。因此,我们那时开始关注流行的消息传递解决方案Kafka。不幸的是,Kafka不能满足我们的要求,特别是在低延迟和高可靠性方面。

在这种情况下,一个新的消息传递引擎来处理更广泛的用例集,从传统的发布/订阅方案到大批量实时零损失容忍交易系统

RocketMQ中主要名词解释

Message:消息,在MQ中进行信息传递的载体。

Producer:生产者,负责消息的生产并发送消息。

Consumer:消费者,用户接收生产者发的消息。

RocketMq特性介绍

RockectMq三种消息发送方式

第一种同步消息发送:同步发送就是指 producer 发送消息后,会在接收到 broker 响应后才继续发下一条消息的通信方式。

java代码示例生产者发送:

 /**
     * 同步消息发送
     * @data 2020/03/16
     */
    public static void main(String[] args) throws Exception {
        //用生产者组名称实例化
        DefaultMQProducer producer = new DefaultMQProducer("Test");
        //指定名称服务器地址
        producer.setNamesrvAddr("127.0.0.1:9876");
        //启动实例
        producer.start();
        //创建一个消息实例,指定主题,标签和消息正文
        for (int i = 0; i < 100; i++) {
            Message msg = new Message("TogicTest", "TagA", ("Hello RocketMq" + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
            //呼叫发送消息以将消息传递给其中一个代理
            SendResult sendResult = producer.send(msg);
        }
        //一旦不再使用生产者实例,请关闭
        producer.shutdown();
    }

第二种异步消息发送:异步发送是指 producer 发出一条消息后,不需要等待 broker 响应,就接着发送下一条消息的通信方式。需要注意的是,不等待 broker 响应,并不意味着 broker 不响应,而是通过回调接口来接收 broker 的响应。

java代码示例生产者发送:

/**
     * 异步消息发送
     * @param args
     * @data 2020/03/16
     */
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("Jodie_Daily_test");
        producer.start();
        producer.setRetryTimesWhenSendAsyncFailed(0);
        for (int i = 0; i < 10000000; i++) {
            try {
                final int index = i;
                Message msg = new Message("Jodie_topic_1023", "TagA", "OrderID188", "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
                //重点在这里 异步发送回调
                producer.send(msg, new SendCallback() {
                    @Override
                    public void onSuccess(SendResult sendResult) {
                        System.out.printf("%-10d OK %s %n", index, sendResult.getMsgId());
                    }
                    @Override
                    public void onException(Throwable e) {
                        System.out.printf("%-10d Exception %s %n", index, e);
                        e.printStackTrace();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        producer.shutdown();
    }

第三种单向消息发送: producer 只负责发送消息,不等待 broker 发回响应结果。

java代码示例生产者发送:

 /**
     * 单向消息发送
     * @data 2020/03/16
     */
        public static void main(String[] args) throws Exception{
            DefaultMQProducer producer = new DefaultMQProducer("Test");
            producer.start();
            for (int i = 0; i < 100; i++) {
                //创建一个消息实例,指定主题,标签和消息正文。
                Message msg = new Message("TopicTest" /* Topic */, "TagA" /* Tag */, ("Hello RocketMQ " +i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
                );
                //呼叫发送消息以将消息传递给其中一个代理。
                producer.sendOneway(msg);
            }
            //一旦不再使用生产者实例,请关闭。
            producer.shutdown();
        }

java代码示例消费者接收:

 /**
     * 普通消费方式
     * @data 2020/03/16
     */
    public static void main(String[] args) throws Exception {

        // 使用consumer group初始化DefaultMQPushConsumer
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name");
        // 指定namesrv
        consumer.setNamesrvAddr("localhost:9876");
        // 订阅topic
        consumer.subscribe("TopicTest", "*");
        // 注册消费消息的回调
        consumer.registerMessageListener(new MessageListenerConcurrently() {

            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
                                                            ConsumeConcurrentlyContext context) {
                System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        // 启动consumer
        consumer.start();
        System.out.printf("Consumer Started.%n");
    }

集群消费&广播消费

集群消费方式:Consumer实例平均分摊消费生产者发送的消息。

 

例如某个 Topic 9 条消息,一个 Consumer ID 3
Consumer 例,那么在集群消 模式下每个 例平均分 ,只消 其中的 3 条消息
 
广播消费方式:Consumer实例各自消费生产者发送的消息。
例如某个 Topic 9 条消息,一个 Consumer ID 3 个,Consumer 实 例,那么在广播消 模式下每个 例都会各自消 9 条消息。
java代码示例生产者:
 /**
     * 广播消息发送
     * @data 2020/03/16
     */
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("test");
        producer.start();
        for (int i = 0; i < 100; i++) {
            Message msg = new Message("TopicTest" /* Topic */, "TagA" /* Tag */, ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET))/* Message body */;
            producer.send(msg);
        }
        producer.shutdown();
    }

 

 

顺序消息

MQ 提供的一种按照 布和消 的消息 , 全局 序消息和分区 序消息
 
java代码示例生产者:
/**
     * 顺序发送消息
     * @data 2020/03/16
     */
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("test");
        producer.start();
        String[] tags = new String[]{"TagA", "TagB", "TagC", "TagD", "TagE"};
        for (int i = 0; i < 100; i++) {
            int orderId = i % 0;
            Message msg = new Message("TopicTest" /* Topic */, "TagA" /* Tag */, ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET);
            producer.send(msg, new MessageQueueSelector() {
                @Override
                public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                    Integer id = (Integer) arg;
                    int index = id % mqs.size();
                    return mqs.get(index);
                }
            }, orderId);
        }
        producer.shutdown();
    }

java代码示例消费者:

/**
     * 顺序消息接收
     * @data 2020/03/16
     */
    public static void main(String[] args) throws Exception {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test");
        consumer.setNamesrvAddr("127.0.0.1:9876");
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        consumer.subscribe("TopicTestjjj", "*");
        consumer.registerMessageListener(new MessageListenerOrderly() {
            AtomicLong consumeTimes = new AtomicLong(0);
            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> mags, ConsumeOrderlyContext context) {
                context.setAutoCommit(false);
                return ConsumeOrderlyStatus.SUCCESS;
            }
        });
        consumer.start();
        System.out.printf("Consumer Started.%n");
    }

常见问题列表

问題:新创建的Consumer ID从哪里开始消费?
  • 如果这个Consumer ID是第一次启动,则会忽略启动之前发送的消息,也就是忽略历史消息,从启动之后发送的消息开始消费。
  • 如果这个Consumer ID是第二次启动,那么从上次消费的位置开始消费。
  • 如果想从特定位置开始消费,可以通过MQ控制台的消费位点重置功能,指定到具体的时间开始消费。毎次重置只针对特定Consumer ID下的特定Topic,不会影响其他Consumer ID。
问題:MQ消费失败如何重新消费消息?
  • 集群消费方式:消费业务逻辑代码如果返回RECONSUME_LATER,或者NULL,或者抛出异常,消息都会走重试流程,至多重试16次,如果重试16次后,仍然失败,则消息进入死信队列。可以通过调用message.getReconsumeTimesO方法来获取消息的重试次数。
  • 广播消费方式:广播消费方式仍然能保证一条消息至少被消费一次,但消费失败后不做重试操作。
问题:没有收到消息怎么办?
  • 确定是否发送
  • 使用Topic按时间范围进行查询,可以查询到一段时间内某Topic收到的所有消息
  • 使用Topic和Message ID对消息进行精准査询
  • 使用Topic和Message Key较为精准地査询具有相同Message Key的一类消息
  • 如果消息存在,可以重推
  • 如果不存在,客户端没有发送成功
问题:MQ能否保证消息不重复?
  • 绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。
问题:MQ消息Body的长度限制是多少?
  • 默认是4M
  • 不建议频繁发送大于1M以上的消息,最大不要超过4M消息
  • 更大的消息建议通过分布式文件存储保存,通过消息发送文件路径作为通
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值