RocketMQ学习笔记

                RocketMQ学习笔记

1. RocketMQ概述

特点:

1.能够保证严格的消息顺序

2.提供丰富的消息拉取模式

3.高效的订阅者水平扩展能力

4.实时的消息订阅机制

5.支持事务消息

6.亿级消息堆积能力

。普通消息

。顺序消息 ----能实现有序消费

。事务消息 ----可以解决分布式事务实现数据最终一致

消息发送方式:

。同步消息-----消息发送中进入同步等待状态,可以保证消息投递一定到达

。异步消息-----想要快速发送消息,又不想丢失的时候可以使用异步消息

producer.send(message, new SendCallback() {
    @Override
    public void onSuccess(SendResult sendResult) {
        System.out.println("消息发送成功");
        System.out.println("sendResult="+sendResult);
    }

    @Override
    public void onException(Throwable throwable) {
          //跳转业务逻辑
          System.out.println("发送异常");
    }
});

​ 。单向消息 -----只发送消息,不等待服务器响应,只发送请求不等待应答。

​ 此方式发送消息的过程耗时非常 短,一般在微秒级别。

​ producer.sendOneway(message);

​ 。批量发送消息

4大中间件:

​ 。消息中间件

​ 。缓存中间件

​ 。文件存储中间件

​ 。搜索中间件

2. RocketMQ下载安装

2.1 RocketMQ下载

一、下载

1、官网下载:

​ http://rocketmq.apache.org/

img

2、百度网盘下载:

下载地址 提取码:0g5a

image-20211017165821302

3、RocketMQ控制台下载:

​ https://github.com/apache/rocketmq-externals/tree/master/

image-20211017162737823

2.2 RocketMQ安装

1、前提条件:

​ 已安装jdk1.8

2、将下载下的安装文件解压到本地磁盘

image-20211017170119169

3、配置环境变量

变量名:ROCKETMQ_HOME

变量值:D:\01program_soft\01install_before\00903RocketMQ\rocketmq-all-4.6.0-bin-release

image-20211017170309477

修改path,添加

img

4、启动

(1)启动NAMESERVER

使用cmd打开命令窗口,进入到rocketmq的bin目录下,执行:start mqnamesrv.cmd,

​ 成功会弹出如下提示框,请勿关闭此框。

image-20211017170442819

(2)启动brocker

同样在rocketmq的bin目录下,执行:start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true , 启动BROKER。成功后会弹出提示框,此框勿关闭。

如果弹出框提示‘错误: 找不到或无法加载主类 xxxxxx’。在bin下找到并打开runbroker.cmd,然后将‘%CLASSPATH%’加上英文双引号。保存并重新执行start语句。如下:

img

修改完毕后,再次启动即可出现成功弹框如下:

image-20211017170540439

三、安装可视化插件

1、github下载:

下载地址 网盘下载:下载地址 提取码:ms2g

2、解压到d:\mq_rocket文件夹下

image-20211017170658836

3.命令行进入到D:\01program_soft\01install_before\00903RocketMQ目录下,

image-20211017170923862

执行命令 java -jar rocketmq-console-ng-1.0.1.jar,回车

image-20211017174014128

出现上面的界面,就说明控制台已经启动成功。

2.3 RocketMQ控制台

​ 控制台地址:http://localhost:17890

​ 用户名: 不用

​ 密 码:不用

image-20211017174448466

3. RocketMQ角色

在这里插入图片描述

部署模型:

img

broker:

​ 。broker面向producer和consumer接收和发送消息

​ 。面向nameServer提交自己的信息

​ 。是消息中间件的消息存储、转发服务器

​ 。每个broker节点,在启动时,都会遍历NameServer列表,与每个NameServer建立连接,注册自己的信息之后定时上报

NameServer:注册中心 == zookeeper

​ 。服务注册

​ 。服务发现

​ 。路由管理

nameServer是服务发现者,集群中各个角色(producer,broker,consumer)都需要定时向nameServer上报自己的状态,以便互相发现彼此,超时不上报的话,nameServer会把它从列表中删除

​ 。为什么不用zookeeper? ----rockermq希望为了提高性能,CAP定理,客户端负载均衡

producer:生产者 -----> master 主: 消息写入

​ slave 从: 发消息 ------> customer:消费者

producer:

​ 。消息的生产者

​ 。通过集群中的其中一个节点建立长连接,获得topic的路由信息,包括topic下面有哪些queue,这些queue分

​ 部在哪些broker上

consumer:

​ 。消息的消费者

​ 。通过NameServer集群获得topic的路由信息,连接到对应的broker上消费消息

​ 注意:由于master和slave都可以读取消息,因此consumer会与master和slave都建立连接

topic和queue的关系:

img

4.点对点模式

4.1 生产者

package com.tangguanlin.rocketmq;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.io.UnsupportedEncodingException;
/**
 * 说明: 点对点模式 生产者
 * 作者:汤观林
 * 日期:2021年10月07日 10时
 */
public class Producer {

    public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException {

        //1.创建DefaultMQProducer
        DefaultMQProducer producer = new DefaultMQProducer("order_demo_producer_group11");

        //2.设置Namesrv地址   注册中心
        producer.setNamesrvAddr("127.0.0.1:9876");

        //3.开启DefaultMQProducer
        producer.start();

        /**
         *  4.创建消息message
         * 参数1 topic 主题
         * 参数2 tags  标记 用于过滤
         * 参数3 keys  消息的唯一值
         * 参数4 body  消息
         */
        Message message = new Message("test1","hello     
                                rocketmq".getBytes(RemotingHelper.DEFAULT_CHARSET));

        //5.同步发送消息  同步:发送--等待回复    --brocker返回的消息
        SendResult result = producer.send(message);
        System.out.println(result);

        //6.关闭DefaultMQProducer
        producer.shutdown();
    }
}

4.2 消费者

package com.tangguanlin.rocketmq;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
/**
 * 说明:点对点模式 消费者
 * 作者:汤观林
 * 日期:2021年10月07日 10时
 */
public class Consumer {
    public static void main(String[] args) throws MQClientException {

        //1.创建DefaultMQProducer
        DefaultMQPushConsumer consumer = new             
                                 DefaultMQPushConsumer("order_demo_producer_group11");

        //2.设置namesrv地址
        consumer.setNamesrvAddr("127.0.0.1:9876");

        /**
         * 3.设置subscribe,这里是要读取的主题信息
         *   参数1  topic 消费的主题
         *   参数2  过滤器    *:不过滤
         */
        consumer.subscribe("test1","*");

        //设置消息拉取上限
        consumer.setConsumeMessageBatchMaxSize(2);

        //4.创建消息监听 messageListener
        consumer.setMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                //循环消息
                for(MessageExt messageExt:list){
                    //获取主题
                    System.out.println("consumer主题:"+messageExt.getTopic());

                    //5.获取消息信息
                    System.out.println("consumer信息内容:"+new String(messageExt.getBody()));
                }

                //6.返回消息读取状态
                //默认情况下,这条消息只会被一个消费者消费  点对点模式
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;  //接收状态确认ack
            }
        });

        //开启consumer
        consumer.start();
    }
}

5. 批量消息发送

image-20211017175625070

可以多条消息打包一起发送,减少网络传输次数提高效率。

​ 。批量消息要求必须具有同一topic,相同消息配置

​ 。不支持延时消息

​ 。建议一个批量消息最好不超过1MB大小

​ 。如果不确定是否超过限制,可以手动计算大小分批发送

5.1 生产者

package com.tangguanlin.rocketmq;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
/**
 * 说明: 批量消息发送 生产者
 * 作者:汤观林
 * 日期:2021年10月07日 10时
 */
public class BatchProducer {

    public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException {

        //1.创建DefaultMQProducer
  DefaultMQProducer producer = new DefaultMQProducer("order_demo_producer_group11");

        //2.设置Namesrv地址   注册中心
        producer.setNamesrvAddr("127.0.0.1:9876");

        //3.开启DefaultMQProducer
        producer.start();

        /**
         *  4.创建消息message
         * 参数1 topic 主题
         * 参数2 tags  标记 用于过滤
         * 参数3 keys  消息的唯一值
         * 参数4 body  消息
         */
        Message message1 = new Message("test2","hello 	
                                    rocketmq1".getBytes(RemotingHelper.DEFAULT_CHARSET));
        Message message2 = new Message("test2","hello 
                                    rocketmq2".getBytes(RemotingHelper.DEFAULT_CHARSET));
        Message message3 = new Message("test2","hello 
                                    rocketmq3".getBytes(RemotingHelper.DEFAULT_CHARSET));

        List<Message> list = new ArrayList<Message>();
        list.add(message1);
        list.add(message2);
        list.add(message3);

        //5.批量同步发送消息  同步:发送--等待回复    --brocker返回的消息
        SendResult result = producer.send(list);
        System.out.println(result);

        //6.关闭DefaultMQProducer
        producer.shutdown();
    }
}

5.2 消费者

package com.tangguanlin.rocketmq;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
/**
 * 说明:点对点模式 消费者
 * 作者:汤观林
 * 日期:2021年10月07日 10时
 */
public class Consumer {
    public static void main(String[] args) throws MQClientException {

        //1.创建DefaultMQProducer
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("order_demo_producer_group11");

        //2.设置namesrv地址
        consumer.setNamesrvAddr("127.0.0.1:9876");

        /**
         * 3.设置subscribe,这里是要读取的主题信息
         *   参数1  topic 消费的主题
         *   参数2  过滤器    *:不过滤
         */
        consumer.subscribe("test1","*");

        //设置消息拉取上限
        consumer.setConsumeMessageBatchMaxSize(2);

        //4.创建消息监听 messageListener
        consumer.setMessageListener(new MessageListenerConcurrently() {
            @Override
     public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, 
                                ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                //循环消息
                for(MessageExt messageExt:list){
                    //获取主题
                    System.out.println("consumer主题:"+messageExt.getTopic());

                    //5.获取消息信息
         System.out.println("consumer信息内容:"+new String(messageExt.getBody()));
                }

                //6.返回消息读取状态
                //默认情况下,这条消息只会被一个消费者消费  点对点模式
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;  //接收状态确认ack
            }
        });

        //开启consumer
        consumer.start();
    }
}

6.异步处理

​ 异步可靠消息

​ 不会阻塞,等待broker的确认

​ 采用事件监听方式接受broker返回的确认

6.1 生产者

package com.tangguanlin.rocketmq;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.io.UnsupportedEncodingException;
/**
 * 说明: 异步处理 生产者
 * 作者:汤观林
 * 日期:2021年10月07日 10时
 */
public class AsynProducer {

    public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException {

        //1.创建DefaultMQProducer
     DefaultMQProducer producer = new DefaultMQProducer("order_demo_producer_group11");

        //2.设置Namesrv地址   注册中心
        producer.setNamesrvAddr("127.0.0.1:9876");

        //3.开启DefaultMQProducer
        producer.start();

        /**
         *  4.创建消息message
         * 参数1 topic 主题
         * 参数2 tags  标记 用于过滤
         * 参数3 keys  消息的唯一值
         * 参数4 body  消息
         */
        Message message = new Message("test1","hello 
                                     rocketmq".getBytes(RemotingHelper.DEFAULT_CHARSET));

        //5.异步可靠消息
         // 不会阻塞,等待broker的确认
         // 采用事件监听方式接受 broker返回的确认
        producer.send(message, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("消息发送成功");
                System.out.println("sendResult="+sendResult);
            }

            @Override
            public void onException(Throwable throwable) {
                  //跳转业务逻辑
                  System.out.println("发送异常");
            }
        });
    }
}

6.2 消费者

package com.tangguanlin.rocketmq;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
/**
 * 说明:异步消息 消费者
 * 作者:汤观林
 * 日期:2021年10月07日 10时
 */
public class Consumer {
    public static void main(String[] args) throws MQClientException {

        //1.创建DefaultMQProducer
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("order_demo_producer_group11");

        //2.设置namesrv地址
        consumer.setNamesrvAddr("127.0.0.1:9876");

        /**
         * 3.设置subscribe,这里是要读取的主题信息
         *   参数1  topic 消费的主题
         *   参数2  过滤器    *:不过滤
         */
        consumer.subscribe("test1","*");

        //设置消息拉取上限
        consumer.setConsumeMessageBatchMaxSize(2);

        //4.创建消息监听 messageListener
        consumer.setMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                //循环消息
                for(MessageExt messageExt:list){
                    //获取主题
                    System.out.println("consumer主题:"+messageExt.getTopic());

                    //5.获取消息信息
                    System.out.println("consumer信息内容:"+new String(messageExt.getBody()));
                }

                //6.返回消息读取状态
                //默认情况下,这条消息只会被一个消费者消费
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;  //接收状态确认ack
            }
        });

        //开启consumer
        consumer.start();
    }
}

7.群组和广播模式

consumer.setMessageModel(MessageMode.BROADCASTING); //设置广播模式

consumer.setMessageModel(MessageMode.CLUSTERING); //集群

//集群 --> 一组 consumer

image-20211017175837534

//广播 所有 consumer

image-20211017175905915

tag ----- 是用来过滤消息 消息分组

key ----- 消息的唯一标识

可以使用tag来过滤消费

在producer中使用Tag:

Message msg = new Message(“TopicTest”,“TagA”,("hello

​ RocketMQ").getBytes(RemotingHelper.DEFAULT_CHARSET));

在consumer中订阅Tag:

consumer.subscribe(“tipicTest”,“TagA|TagB”); //代表订阅Topic下的所有消息

8.sql过滤

image-20211017180233984

broker.conf文件增加属性:

​ EnablePropertyFilter =true

8.1 生产者

package com.tangguanlin.rocketmq;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.io.UnsupportedEncodingException;
/**
 * 说明: sql过滤 生产者
 * 作者:汤观林
 * 日期:2021年10月07日 10时
 */
public class SQLProducer {

    public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException {

        //1.创建DefaultMQProducer
        DefaultMQProducer producer = new DefaultMQProducer("sql_producer_group11");

        //2.设置Namesrv地址   注册中心
        producer.setNamesrvAddr("127.0.0.1:9876");

        //3.开启DefaultMQProducer
        producer.start();

        /**
         *  4.创建消息message
         * 参数1 topic 主题
         * 参数2 tags  标记 用于过滤
         * 参数3 keys  消息的唯一值
         * 参数4 body  消息
         */
        for(int i=0;i<100;i++){
            Message message = new Message("sql",("hello   	        
                                 rocketmq"+i).getBytes(RemotingHelper.DEFAULT_CHARSET));
            message.putUserProperty("age",String.valueOf(i));

            //5.同步发送消息  同步:发送--等待回复    --brocker返回的消息
            SendResult result = producer.send(message);
            System.out.println(result);
        }

        //6.关闭DefaultMQProducer
        producer.shutdown();
    }
}

8.2 消费者

package com.tangguanlin.rocketmq;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.MessageSelector;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
/**
 * 说明:sql过滤 消费者
 * 作者:汤观林
 * 日期:2021年10月07日 10时
 */
public class SQLConsumer {
    public static void main(String[] args) throws MQClientException {

        //1.创建DefaultMQProducer
        DefaultMQPushConsumer consumer = new 
                                       DefaultMQPushConsumer("sql_producer_group11");

        //2.设置namesrv地址
        consumer.setNamesrvAddr("127.0.0.1:9876");

      MessageSelector messageSelector = MessageSelector.bySql("age >= 18 and age <= 28");

        /**
         * 3.设置subscribe,这里是要读取的主题信息
         *   参数1  topic 消费的主题
         *   参数2  过滤器    *:不过滤
         */
        consumer.subscribe("sql",messageSelector);

        //设置消息拉取上限
        consumer.setConsumeMessageBatchMaxSize(2);

        //4.创建消息监听 messageListener
        consumer.setMessageListener(new MessageListenerConcurrently() {
            @Override
  public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, 
                      ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                //循环消息
                for(MessageExt messageExt:list){
                    //获取主题
                    System.out.println("consumer主题:"+messageExt.getTopic());

                    //5.获取消息信息
                    System.out.println("consumer信息内容:"+new 
                                                      String(messageExt.getBody()));
                }

                //6.返回消息读取状态
                //默认情况下,这条消息只会被一个消费者消费  点对点模式
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;  //接收状态确认ack
            }
        });

        //开启consumer
        consumer.start();
    }
}

9.RocketMQ事务消息

image-20211010151011278

image-20211017180537710

2pc: 2次提交 第1次提交时,不会立即生效,立即消费

​ RocketMQ事务消息用的就是2pc这种方式

tcc: try(锁住)–comfirm(确认)–cancel(取消)

producer----开启事务 ----发送消息(half message) ----broker:打标记—HFM发送确认

分布式系统中的事务可以使用2pc、TCC(try,confirm,cancle)来解决分布式系统中的消息原子性

RocketMQ4.3+提供分布式事务功能,通过RocketMQ事务消息能达到分布式事务的最终一致

RockMQ实现方式:

​ Half Message:预处理消息,当broker收到此消息后,会存储到RMQ_SYS_RANS_HALF_TOPIC的消息消费队列中

​ 检查事务状态:broker会开启一个定时任务,消息RMQ_SYS_TRANS_HALF_TOPIC队列中的消息,每次执行任务会想向消息发送者确认执行状态(提交、回滚、未知),如果是未知,等待下一次回调。

​ 超时:如果超过回调次数,默认回滚消息

image-20211017180701439

TransactionListener的两个方法:

​ executeLocalTransaction:半消息发送成功触发此方法来执行本地事务

​ checkLocalTransaction: brocker将发送检查消息来检查事务状态,并将调用此方法来获取本地事务状态

本地事务执行状态:

​ 执行成功,确认提交:LocalTransactionState.COMMIT_MESSAGE

​ 回滚消息,broker端会删除版消息:LocalTransactionState.ROLLBACK_MESSAGE

​ 暂时为未知状态,等待broker回调:LocalTransactionState.UNKNOW

​ 问题:producer.send()放入事务里,还是事务外?

9.1 生产者

package com.tangguanlin.rocketmq;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.*;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.io.UnsupportedEncodingException;
/**
 * 说明: 事务消息 生产者
 * 作者:汤观林
 * 日期:2021年10月07日 10时
 */
public class TransactionProducer {

    public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException {

        //1.创建DefaultMQProducer
        TransactionMQProducer producer = new 
                                TransactionMQProducer("transaction_producer_group11");

        //2.设置Namesrv地址   注册中心
        producer.setNamesrvAddr("127.0.0.1:9876");

        //回调
        producer.setTransactionListener(new TransactionListener() {
            //事务操作---几个方法  是原子性操作
            @Override
       public LocalTransactionState executeLocalTransaction(Message message, Object o) {
                //执行本地业务
                System.out.println("===executeLocalTransaction====");
                System.out.println("msg:="+new String(message.getBody()));
                System.out.println("msg:="+message.getTransactionId());  //事务id

            //----原子性操作--------
                try{
                    //a();  提交注册信息
                    //b();  写入数据库
                    //c();  新用户 发短信

                }catch (Exception e){
                    return LocalTransactionState.ROLLBACK_MESSAGE;
                }
                return LocalTransactionState.COMMIT_MESSAGE;
            }

            @Override
            public LocalTransactionState checkLocalTransaction(MessageExt messageExt) {
                //broker端 回调,检查事务

                System.out.println("===checkLocalTransaction====");
                System.out.println("msg:="+new String(messageExt.getBody()));
                System.out.println("msg:="+messageExt.getTransactionId());  //事务id

                    //根据那条message自己写逻辑检查

                    //事务执行成功
                    return LocalTransactionState.COMMIT_MESSAGE;
                    //等会儿
                    //return LocalTransactionState.UNKNOW;
                    //回滚
                    //return LocalTransactionState.ROLLBACK_MESSAGE;
            }
        });

        //3.开启DefaultMQProducer
        producer.start();

        /**
         *  4.创建消息message
         * 参数1 topic 主题
         * 参数2 tags  标记 用于过滤
         * 参数3 keys  消息的唯一值
         * 参数4 body  消息
         */
        Message message = new Message("transaction","hello 
                                  transaction".getBytes(RemotingHelper.DEFAULT_CHARSET));

        //5.同步事务消息
        SendResult result = producer.sendMessageInTransaction(message,null);
        System.out.println(result);

        //6.关闭DefaultMQProducer
        //producer.shutdown();
    }
}

9.2 消费者

package com.tangguanlin.rocketmq;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
/**
 * 说明:事务消息 消费者
 * 作者:汤观林
 * 日期:2021年10月07日 10时
 */
public class TransactionConsumer {
    public static void main(String[] args) throws MQClientException {

        //1.创建DefaultMQProducer
        DefaultMQPushConsumer consumer = new 
                                   DefaultMQPushConsumer("order_demo_producer_group11");

        //2.设置namesrv地址
        consumer.setNamesrvAddr("127.0.0.1:9876");

        /**
         * 3.设置subscribe,这里是要读取的主题信息
         *   参数1  topic 消费的主题
         *   参数2  过滤器    *:不过滤
         */
        consumer.subscribe("test1","*");

        //设置消息拉取上限
        consumer.setConsumeMessageBatchMaxSize(2);

        //4.创建消息监听 messageListener
        consumer.setMessageListener(new MessageListenerConcurrently() {
            @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, 
                           ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                //循环消息
                for(MessageExt messageExt:list){
                    //获取主题
                    System.out.println("consumer主题:"+messageExt.getTopic());

                    //5.获取消息信息
               System.out.println("consumer信息内容:"+new String(messageExt.getBody()));
                }

                //6.返回消息读取状态
                //默认情况下,这条消息只会被一个消费者消费  点对点模式
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;  //接收状态确认ack
            }
        });

        //开启consumer
        consumer.start();
    }
}

10.重试机制

producer

​ 默认超时时间 private int sendMsgTimeOut = 30000;

同步发送时,重试次数:默认是2

​ producer.setRetryTimesWhenSendFaild(3);

异步发送时,重试次数: 默认是2

​ producer.setRetryTimeWhenSendAsyncFailed();

Consumer

​ 消费超时 单位分钟

​ consumer.setConsumerTimeOut();

​ 发送ack,消费失败

​ RECONSUMER_LATER

broker投递

​ 只有在消息模式为MessageModelCLUSTERING集群模式时,broker才会自动进行重试,广播消息不重试,重投使用messageDelayLevel

默认值:

​ messageDelayLevel: 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

11.重复消费

image-20211017180913426

11.1 消息丢失

SendResult

​ producer在发送同步/异步可靠消息后,会接收到SendResult,表示消息发送成功。

​ SendResult其中属性sendStatus表示了broker是否真正完成了消息存储

​ 当sendStatus=‘ok’的时候,应该重新发送消息,避免丢失

11.2 消息重复消费

​ 影响消息正常发送和消费的重要原因是网络的不确定性。

引起重复消费的原因:

​ ACK

​ 正常情况下在consumer真正消费完消息后应该发送ack,通知broker该消息正常消费,从queue中剔除,

​ 当ack因为网络原因无法发送到broker,broker会认为此条消息没有被消费,此后会被开启消息重投机制把消息再次投递到consumer

​ group

​ 在SLUSTERING模式下,消息在broker中会保证相同group的consumer消费一次(每个group的一个consumer),但是针对不同group的consumer会推送多次

​ 解决方案:

​ 数据库表

​ 处理消息前,使用消息主键在表中带有约束的字段insert

​ Map

​ 单机时可以使用map concurrentHashMap -->putIfAbsent

​ Redis

​ 使用主键或set操作 比如用messageId作为主键

12.顺序消费

image-20211017181101450

1.同一个topic(一个topic包含多个queue:数组 先进先出)

2.同一个queue

3.发消息的时候一个线程去发送消息

。消费的时候一个线程 消费一个queue里的消息

。多个queue只能保证单个queue里的顺序

12.1 生产者

选择具体的queue:

package com.tangguanlin.rocketmq;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.io.UnsupportedEncodingException;
import java.util.List;
/**
 * 说明: 指定queue 生产者
 * 作者:汤观林
 * 日期:2021年10月07日 10时
 */
public class QueueProducer {

    public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException {

        //1.创建DefaultMQProducer
        DefaultMQProducer producer = new DefaultMQProducer("queue_producer_group11");

        //2.设置Namesrv地址   注册中心
        producer.setNamesrvAddr("127.0.0.1:9876");

        //3.开启DefaultMQProducer
        producer.start();

        /**
         *  4.创建消息message
         * 参数1 topic 主题
         * 参数2 tags  标记 用于过滤
         * 参数3 keys  消息的唯一值
         * 参数4 body  消息
         */

        for(int i=0;i<20;i++){
            Message message = new Message("queue",("hello rocketmq"+i).getBytes(RemotingHelper.DEFAULT_CHARSET));

            //5.发送消息  queue选择器 向topic哪个queue去写消息
            SendResult result = producer.send(message, new MessageQueueSelector() {
                //手动选择一个queue
                @Override
         public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
                    MessageQueue queue = list.get((Integer) o);
                    //选好的queue
                    return queue;
                }
                //arg:第几个queue  timeout:超时时间
            },0,20000);
        }
        for(int i=0;i<20;i++){
     Message message = new Message("queue",("hello 
                        rocketmq"+i).getBytes(RemotingHelper.DEFAULT_CHARSET));

            //5.发送消息  queue选择器 向topic哪个queue去写消息
            SendResult result = producer.send(message, new MessageQueueSelector() {
                //手动选择一个queue
                @Override
       public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
                    MessageQueue queue = list.get((Integer) o);
                    //选好的queue
                    return queue;
                }
                //arg:第几个queue  timeout:超时时间
            },1,20000);
        }

        //6.关闭DefaultMQProducer
        producer.shutdown();
    }
}

12.2 消费者

package com.tangguanlin.rocketmq;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.*;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
/**
 * 说明:rocketmq 顺序消费者
 * 作者:汤观林
 * 日期:2021年10月07日 10时
 */
public class QueueConsumer {
    public static void main(String[] args) throws MQClientException {

        //1.创建DefaultMQProducer
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("queue_producer_group11");

        //2.设置namesrv地址
        consumer.setNamesrvAddr("127.0.0.1:9876");

        /**
         * 3.设置subscribe,这里是要读取的主题信息
         *   参数1  topic 消费的主题
         *   参数2  过滤规则
         */
        consumer.subscribe("queue","*");

        //开启最大线程数  最好用一个线程去消费,保持顺序消费
        consumer.setConsumeMessageBatchMaxSize(2);

        //4.创建消息监听 messageListener
        consumer.setMessageListener(new MessageListenerOrderly() {
                        //单个queue是单个线程
                        //多个queue是多个线程
                        @Override
          public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, 
                                          ConsumeOrderlyContext consumeOrderlyContext) {
                           //读取消息
                            for(MessageExt messageExt:list){
                                //获取主题
                                String topic  = messageExt.getTopic();
                                 //获取标签
                                String tags = messageExt.getTags();

                                String result = new String(messageExt.getBody());
                                System.out.println("order消息的主题="+topic +" order消息的内
                                 容="+result+" 线程名称"+ 
                                  Thread.currentThread().getName()+"    
                                   queueId="+messageExt.getQueueId());
                            }

                            return ConsumeOrderlyStatus.SUCCESS;
                        }
                    }
        );

        //开启consumer
        consumer.start();
    }
}

13.长轮询机制

1.offset 消息的数组下标

在这里插入图片描述

​ queue.getOffset(); //队列的下标

offset的存储与加载:
rocketMQ的broker端中,offset的是以json的形式持久化到磁盘文件中,文件路径为

​ ${user.home}/store/config/consumerOffset.json

在这里插入图片描述

在这里插入图片描述

2.长轮询机制

image-20211017150135690

image-20211017150833051

2.1 轮询: http协议

​ 没发起一次请求,都返回数据,没有数据就返回空数据 消耗大,特别影响性能

2.2 长连接:

​ 客户端和服务器一直保持连接,当客户端多了之后,服务器压力很大,很影响服务器性能,

​ 而且,消息会不停的给某一个客户端,导致该客户端消化不了,不能按需消费,主动权在服务器打算

2.3 长轮询机制:

​ 客户端发起请求,服务器端有消息就返回,没有消息就挂起,等有消息了后返回给客户端,

​ 主动权在客户端,客户端按需消费,消费完一个消息,再去跟服务器端要一个消息。这个机制就很美好。

​ RocketMQ就是采用的长轮询机制的。

14.SpringBoot整合RocketMQ

(略)

15.RocketMQ的集群(略)

      容="+result+" 线程名称"+ 
                              Thread.currentThread().getName()+"    
                               queueId="+messageExt.getQueueId());
                        }

                        return ConsumeOrderlyStatus.SUCCESS;
                    }
                }
    );

    //开启consumer
    consumer.start();
}

}




# 13.长轮询机制

 1.offset  消息的数组下标

[外链图片转存中...(img-ct9gCXKh-1634466791553)]

​      queue.getOffset();       //队列的下标

offset的存储与加载:
              rocketMQ的broker端中,offset的是以json的形式持久化到磁盘文件中,文件路径为                 

​             ${user.home}/store/config/consumerOffset.json

[外链图片转存中...(img-SjthNjkT-1634466791554)]

[外链图片转存中...(img-g1H6tn2u-1634466791555)]

2.长轮询机制

[外链图片转存中...(img-0AVk3Lud-1634466791557)]

[外链图片转存中...(img-LVlVgEEa-1634466791558)]

2.1  轮询: http协议

​       没发起一次请求,都返回数据,没有数据就返回空数据  消耗大,特别影响性能



2.2  长连接:

​      客户端和服务器一直保持连接,当客户端多了之后,服务器压力很大,很影响服务器性能,

​     而且,消息会不停的给某一个客户端,导致该客户端消化不了,不能按需消费,主动权在服务器打算



2.3  长轮询机制:

​      客户端发起请求,服务器端有消息就返回,没有消息就挂起,等有消息了后返回给客户端,

​     主动权在客户端,客户端按需消费,消费完一个消息,再去跟服务器端要一个消息。这个机制就很美好。

​    RocketMQ就是采用的长轮询机制的。     



# 14.SpringBoot整合RocketMQ

  (略)







# 15.RocketMQ的集群(略)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值