什么是RocketMQ?

消息队列 RocketMQ 版(原ONS)是阿里云基于 Apache RocketMQ 构建的低延迟、高并发、高可用、高可靠的分布式消息中间件。最初由阿里巴巴自研并捐赠给 Apache 基金会,服务阿里集团13年,覆盖全集团所有业务。作为双十一交易核心链路的官方指定产品,支撑千万级并发、万亿级数据洪峰,历年刷新大规模交易消息流转记录。

工作流程图:

rocketmq的grafana模板 rocketmq流程_rocketmq的grafana模板

工作流程文字描述:

1.启动namesvr后,等待broker发送心跳,把broker的ip信息管理起来

2.启动broker,向namesvr发送心跳

3.启动生产者producer,随机和某个namesvr建立长连接,建立topic,从namesvr中获取对应topic的broker的ip,先broker发送消息

4.启动消费者consumer,随机和某个namesvr建立长连接,broker推送新消息,consumer获取已订阅的topic中的消息进行消费

准备工作:

添加依赖:
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>4.4.0</version>
</dependency>
入门案例:

生产者

public class Producer {
    public static void main(String[] args) throws Exception{
        //1 创建一个生产者对象, 并且指定一个生产者组
        DefaultMQProducer defaultMQProducer =new DefaultMQProducer("rocketmq-producer");
        //2 设置名字服务的地址
        defaultMQProducer.setNamesrvAddr("localhost:9876");
        //3 启动生产者
        defaultMQProducer.start();
        //4 创建一个消息
        Message message = new Message("mq1",("hello RocketMQ---"+System.currentTimeMillis()).getBytes());
        //5 发送消息
        defaultMQProducer.send(message);
        //6 关闭连接
        defaultMQProducer.shutdown();
    }
}

消费者

public class Consumer {
    public static void main(String[] args) throws Exception{
        //创建一个拉取消息的消费者对象
        DefaultMQPushConsumer Consumer =new DefaultMQPushConsumer("rocketmq-consumer");
        //设置名字地址
        Consumer.setNamesrvAddr("localhost:9876");
        //绑定消息的主题
        Consumer.subscribe("mq1","*");
        //消费者监听处理消息方法
        Consumer.registerMessageListener(new MessageListenerConcurrently() {
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                //list就是订阅topic的消息集合
                for (MessageExt messageExt : list) {
                    String msg = new String(messageExt.getBody());
                    System.out.println(msg);
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //启动消费者
        Consumer.start();
    }
}

原生API的使用

发送方式
同步发送

同步发送,有返回值,发送消息后需要等待消息持久化到磁盘

defaultMQProducer.send(msg);

public class SyncProducer {
   public static void main(String[] args) throws Exception {
       // 实例化消息生产者Producer
        DefaultMQProducer defaultMQProducer = new DefaultMQProducer("rocketmq-producer");
       // 设置NameServer的地址
        defaultMQProducer.setNamesrvAddr("localhost:9876");
       // 启动Producer实例
        defaultMQProducer.start();
        for (int i = 0; i <10 ; i++) {
            Message msg = new Message("mq2", ("rocketmq-sync---" + System.currentTimeMillis()).getBytes());
            SendResult send = defaultMQProducer.send(msg);
            System.out.println(send.getMsgId());
        }

       // 如果不再发送消息,关闭Producer实例。
        defaultMQProducer.shutdown();
    }
}
异步发送

异步发送,有返回值,但它发送消息后,不需要等待消息持久化完成,就可以执行其他操作,直到sendCallBack的回调再做相应的处理

producer.send(msg, new SendCallback() {
public void onSuccess(SendResult sendResult) {}

public void onException(Throwable throwable) {}
});

public class AsyncProducer {
   public static void main(String[] args) throws Exception {
       // 实例化消息生产者Producer
        DefaultMQProducer producer = new DefaultMQProducer("rocketmq-producer");
       // 设置NameServer的地址
      producer.setNamesrvAddr("localhost:9876");
       // 启动Producer实例
        producer.start();
      final CountDownLatch count = new CountDownLatch(10);
      for (int i = 0; i < 10; i++) {
           // 创建消息,并指定Topic,Tag和消息体
           Message msg = new Message("mq3", ("rocketmq-async " + System.currentTimeMillis()).getBytes());
           //发送同步消息到一个Broker
           producer.send(msg, new SendCallback() {
            public void onSuccess(SendResult sendResult) {
               System.out.println("发送成功--"+sendResult.getMsgId());
               count.countDown();
            }

            public void onException(Throwable throwable) {
               System.out.println("发送失败");
               count.countDown();
            }
         });
       }
      count.await();
       // 如果不再发送消息,关闭Producer实例。
       producer.shutdown();
    }
}
一次性发送

一次性发送没有返回值的,因为它不关心消息是否成功持久化到磁盘中

producer.sendOneway(msg);

public class OneWayProducer {
    public static void main(String[] args) throws Exception {
        // 实例化消息生产者Producer
        DefaultMQProducer producer = new DefaultMQProducer("rocketmq-producer");
        // 设置NameServer的地址
        producer.setNamesrvAddr("localhost:9876");
        // 启动Producer实例
        producer.start();
        for (int i = 0; i < 10; i++) {
            // 创建消息,并指定Topic,Tag和消息体
            Message msg = new Message("mq4", ("rocketmq-oneway---" + System.currentTimeMillis()).getBytes());
            //发送同步消息到一个Broker
            producer.sendOneway(msg);
        }
        // 如果不再发送消息,关闭Producer实例。
        producer.shutdown();
    }
}
消费模式
集群

设置集群消费模式:consumer.setMessageModel(MessageModel.CLUSTERING);

集群模式下,某一条消息只能被某一个消费者消费

public class Consumer {
    public static void main(String[] args) throws Exception{
        //创建一个拉取消息的消费者对象
        DefaultMQPushConsumer consumer =new DefaultMQPushConsumer("rocketmq-consumer");
        //设置消费模式
        consumer.setMessageModel(MessageModel.CLUSTERING);
        //设置名字地址
        consumer.setNamesrvAddr("localhost:9876");
        //绑定消息的主题
        consumer.subscribe("mq5","*");
        //消费者监听处理消息方法
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                for (MessageExt messageExt : list) {
                    String msg = new String(messageExt.getBody());
                    System.out.println(msg);
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //启动消费者
        consumer.start();
    }
}
广播:

设置广播消费模式:consumer.setMessageModel(MessageModel.BROADCASTING);

广播模式下,每条消息都会被所有消费者消费一次

public class Consumer {
    public static void main(String[] args) throws Exception{
        //创建一个拉取消息的消费者对象
        DefaultMQPushConsumer consumer =new DefaultMQPushConsumer("rocketmq-consumer");
        //设置消费模式
        consumer.setMessageModel(MessageModel.BROADCASTING);
        //设置名字地址
        consumer.setNamesrvAddr("localhost:9876");
        //绑定消息的主题
        consumer.subscribe("mq6","*");
        //消费者监听处理消息方法
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                for (MessageExt messageExt : list) {
                    String msg = new String(messageExt.getBody());
                    System.out.println(msg);
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //启动消费者
        consumer.start();
    }
}
消费方式

一般情况下,我们都是使用推送的方式。

推送

上面所有的消费者都属于推送消费方式,这里就不说了,来看下拉取消费方式就行了

拉取
public class PullConsumer {
    public static void main(String[] args) throws Exception{
        //创建一个拉取消息的消费者对象
        DefaultMQPullConsumer consumer =new DefaultMQPullConsumer("rocketmq-consumer");
        //设置名字地址
        consumer.setNamesrvAddr("localhost:9876");
        //启动消费者
        consumer.start();
        
        /**
         * messageQueue   1:topic  2:brokerName  3:queueId
         * subExpression  过滤表达式
         * offset         从第offset条消息开始拉取
         * maxNums        拉取多少条
         */
        PullResult pullResult = consumer.pull(new MessageQueue("mq7", "broker-a", 0),
                                                        "*", 0, 10);
        List<MessageExt> messageExts = pullResult.getMsgFoundList();
        for (MessageExt messageExt : messageExts) {
            System.out.println("拉取消费方式:"+new String(messageExt.getBody()));
        }

        consumer.shutdown();
    }
}
延迟消息

设置延时消息等级:msg.setDelayTimeLevel(2); 1为1s,2为5s,3为10s…

public class Producer {
   public static void main(String[] args) throws Exception {
       // 实例化消息生产者Producer
        DefaultMQProducer producer = new DefaultMQProducer("rocketmq-producer");
       // 设置NameServer的地址
        producer.setNamesrvAddr("localhost:9876");
       // 启动Producer实例
        producer.start();
        for (int i = 0; i <5 ; i++) {
            Message msg = new Message("mq8", ("rocketmq-delay---" + System.currentTimeMillis()).getBytes());
            // 设置延时等级2,这个消息将在5s之后发送(现在只支持固定的几个时间,详看delayTimeLevel)
            msg.setDelayTimeLevel(2);
            SendResult send = producer.send(msg);
            System.out.println(send.getMsgId());
        }

       // 如果不再发送消息,关闭Producer实例。
        producer.shutdown();
    }
}
消息过滤
Tag消息过滤

生产者

//TagB作为过滤条件
Message msg = new Message(“mq9”,“TagB”,(“rocketmq-filter-tag—” + System.currentTimeMillis()).getBytes());

public class Producer {
   public static void main(String[] args) throws Exception {
       // 实例化消息生产者Producer
        DefaultMQProducer defaultMQProducer = new DefaultMQProducer("rocketmq-producer");
       // 设置NameServer的地址
        defaultMQProducer.setNamesrvAddr("localhost:9876");
       // 启动Producer实例
        defaultMQProducer.start();

        //TagB作为过滤条件
        Message msg = new Message("mq9","TagB",("rocketmq-filter-tag---" + System.currentTimeMillis()).getBytes());
        SendResult send = defaultMQProducer.send(msg);
        System.out.println(send.getMsgId());

       // 如果不再发送消息,关闭Producer实例。
        defaultMQProducer.shutdown();
    }
}

消费者

Consumer.subscribe(“mq9”,“TagA || TagB”);

绑定消息的主题,参数一:topic,参数二:过滤表达式,每个Tag之间用||分割,意思是只消费该topic中,Tag为TagA或者TagB的消息,假如生产者发送了Tag为TagC的消息,它就不会消费

public class Consumer {
    public static void main(String[] args) throws Exception{
        //创建一个拉取消息的消费者对象
        DefaultMQPushConsumer Consumer =new DefaultMQPushConsumer("rocketmq-consumer");
        //设置名字地址
        Consumer.setNamesrvAddr("localhost:9876");
        //绑定消息的主题
        Consumer.subscribe("mq9","TagA || TagB");
        //消费者监听处理消息方法
        Consumer.registerMessageListener(new MessageListenerConcurrently() {
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                for (MessageExt messageExt : list) {
                    String msg = new String(messageExt.getBody());
                    System.out.println(msg);
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //启动消费者
        Consumer.start();
    }
}
Sql92消息过滤

生产者

//设置sql92表达式,相当于:age=21

msg.putUserProperty(“age”,String.valueOf(21));

public class Producer {
   public static void main(String[] args) throws Exception {
       // 实例化消息生产者Producer
        DefaultMQProducer defaultMQProducer = new DefaultMQProducer("rocketmq-producer");
       // 设置NameServer的地址
        defaultMQProducer.setNamesrvAddr("localhost:9876");
       // 启动Producer实例
        defaultMQProducer.start();

        Message msg = new Message("mq10",("rocketmq-filter-sql92---" + System.currentTimeMillis()).getBytes());
       //设置sql92表达式
        msg.putUserProperty("age",String.valueOf(21));

        SendResult send = defaultMQProducer.send(msg);
        System.out.println(send.getMsgId());


       // 如果不再发送消息,关闭Producer实例。
        defaultMQProducer.shutdown();
    }
}

消费者

//绑定消息的主题,通过sql92表达式过滤,这里的意思是这个消费者只会消费topic为mq10,且表达式中age>18的消息,上面的消费者发送的消息中age设置为21了,所以它会消费这条消息
Consumer.subscribe(“mq10”, MessageSelector.bySql(“age>18”));

public class Consumer {
    public static void main(String[] args) throws Exception{
        //创建一个拉取消息的消费者对象
        DefaultMQPushConsumer Consumer =new DefaultMQPushConsumer("rocketmq-consumer");
        //设置名字地址
        Consumer.setNamesrvAddr("localhost:9876");
        //绑定消息的主题
        Consumer.subscribe("mq10", MessageSelector.bySql("age>18"));
        //消费者监听处理消息方法
        Consumer.registerMessageListener(new MessageListenerConcurrently() {
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                for (MessageExt messageExt : list) {
                    String msg = new String(messageExt.getBody());
                    System.out.println(msg);
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //启动消费者
        Consumer.start();
    }
}

SpringBoot集成RocketMQ

创建生产者工程
添加依赖:
<!--父工程-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.2.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-spring-boot-starter</artifactId>
        <version>2.0.3</version>
    </dependency>
</dependencies>

在resources目录下创建application.properties文件

rocketmq.name-server=127.0.0.1:9876
rocketmq.producer.group=my-group
server.port=9090
写一个生产者的Cotroller:
@RestController
public class ProducerController {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;


   /*
   *  同步发送
   */
    @RequestMapping("/sync")
    public String sync(String msg){
        rocketMQTemplate.syncSend("01-sync",msg);
        return "发送成功:"+msg;
    }

    /*
     *  异步发送
     */
    @RequestMapping("/async")
    public String async(String msg){
        rocketMQTemplate.asyncSend("02-async",msg, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println(sendResult.getMsgId());
            }

            @Override
            public void onException(Throwable throwable) {
                System.out.println(throwable.getMessage());
            }
        });
        return "发送成功:"+msg;
    }

    /*
     *  一次性发送
     */
    @RequestMapping("/oneWay")
    public String oneWay(String msg){
        rocketMQTemplate.sendOneWay("03-oneWay",msg);
        return "发送成功:"+msg;
    }

    /*
     *  集群消费模式
     *  在集群了两个消费者的情况下,我们发送10条消息来进行测试,
     *  这10条消息会被两个消费者分工消费,一条消息只会被某个消费者消费
     */
    @RequestMapping("/clustering")
    public String clustering(String msg){
        for (int i = 0; i <10 ; i++) {
            rocketMQTemplate.syncSend("clustering",msg);
        }
        return "发送成功:"+msg;
    }

    /*
     *  广播消费模式
     *  有两个消费者的广播模式下,我们发送一条消息,发现两个消费者都去消费了这条数据
     */
    @RequestMapping("/broad")
    public String broad(String msg){
        rocketMQTemplate.syncSend("broad",msg);
        return "发送成功:"+msg;
    }

    /*
     * 延迟发送
     * message:    消息
     * timeout:   超时时间,单位毫秒
     * delayLevel:延迟等级 1为1s,2为5s,3为10s.......
     * rocketMQTemplate.syncSend("delay",MessageBuilder.withPayload(msg).build(),3000,2);
     */
    @RequestMapping("/delay")
    public String delay(String msg){
        //Message message = new Message("delay",msg.getBytes());
        rocketMQTemplate.syncSend("delay",
                MessageBuilder.withPayload(msg).build(),3000,2);
        return "发送成功:"+msg;
    }


    /*
     *  Tag过滤
     * destination参数topic和Tag用冒号":"分割:topic:Tag  => filter-tag:TagA
     *  rocketMQTemplate.syncSend("filter-tag"+":"+"TagA",msg);
     */
    @RequestMapping("/tag")
    public String tag(String msg){
        rocketMQTemplate.syncSend("filter-tag"+":"+"TagA",msg);
        return "发送成功:"+msg;
    }

    /*
     *  sql92过滤
     *  map参数把key-value作为过滤条件 =>  age=20
     *  rocketMQTemplate.convertAndSend("filter-sql92",msg,map);
     */
    @RequestMapping("/sql92")
    public String sql92(String msg){
        HashMap<String, Object> map = new HashMap<>();
        map.put("age",20);
        rocketMQTemplate.convertAndSend("filter-sql92",msg,map);
        return "发送成功:"+msg;
    }
}
创建一个消费者的工程
添加依赖:
<!--父工程-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.2.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-spring-boot-starter</artifactId>
        <version>2.0.3</version>
    </dependency>
</dependencies>

在resources目录下创建application.properties文件

rocketmq.name-server=127.0.0.1:9876
server.port=9091
编写consumer来测试

在SpringBoot项目中,使用@RocketMQMessageListener注解来设置topic,消费分组,消费模式,过滤类型,过滤表达式…

1.创建consumer1测试同步发送

@Component
@RocketMQMessageListener(
        topic = "01-sync",
        consumerGroup = "wolfcode-consumer"
)

public class Consumer implements RocketMQListener<String>{
    @Override
    public void onMessage(String s) {
        System.out.println(s);
    }
}

访问http://127.0.0.1:9090/sync?msg=sync123,进行测试

2.创建consumer2测试异步发送

@Component
@RocketMQMessageListener(
        topic = "02-async",
        consumerGroup = "wolfcode-consumer"
)

public class Consumer implements RocketMQListener<String>{
    @Override
    public void onMessage(String s) {
        System.out.println(s);
    }
}

访问http://127.0.0.1:9090/async?msg=async123,进行测试

3.创建consumer3测试一次性发送

@Component
@RocketMQMessageListener(
        topic = "03-oneWay",
        consumerGroup = "wolfcode-consumer"
)

public class Consumer implements RocketMQListener<String>{
    @Override
    public void onMessage(String s) {
        System.out.println(s);
    }
}

访问http://127.0.0.1:9090/oneWay?msg=oneWay123,进行测试

4.创建consumer4和consumer5测试集群模式

@Component
@RocketMQMessageListener(
        topic = "broad",
        consumerGroup = "wolfcode-consumer",
        messageModel = MessageModel.CLUSTERING
)

public class Consumer4 implements RocketMQListener<String>{
    @Override
    public void onMessage(String s) {
        System.out.println("消费者1:"+s);
    }
}
@Component
@RocketMQMessageListener(
        topic = "broad",
        consumerGroup = "wolfcode-consumer",
        messageModel = MessageModel.CLUSTERING
)

public class Consumer5 implements RocketMQListener<String>{
    @Override
    public void onMessage(String s) {
        System.out.println("消费者2:"+s);
    }
}

访问http://127.0.0.1:9090/clustering?msg=clustering123,发现一条消息只会被某个消费者消费

5.创建consumer4和consumer5测试广播模式

这里跟上面的集群一样,只需要把messageModel 修改为 MessageModel.BROADCASTING就可以了

访问http://127.0.0.1:9090/broad?msg=broad123,发现两个消费者都消费了同一条消息

6.创建consumer6测试消息延迟

@Component
@RocketMQMessageListener(
        topic = "delay",
        consumerGroup = "wolfcode-consumer1"
)

public class Consumer6 implements RocketMQListener<String>{
    @Override
    public void onMessage(String s) {
        System.out.println(s);
    }
}

访问http://127.0.0.1:9090/delay?msg=delay123,发现消费者在消息发送成功,过了5秒钟才去消费这条消息

7.创建consumer7测试Tag消息过滤

@Component
@RocketMQMessageListener(
        topic = "filter-tag",
        consumerGroup = "wolfcode-consumer1",
        selectorType = SelectorType.TAG,
        selectorExpression = "TagA"
)

public class Consumer7 implements RocketMQListener<String>{
    @Override
    public void onMessage(String s) {
        System.out.println(s);
    }
}

访问http://127.0.0.1:9090/tag?msg=tag123,发现消费者只会消费topic = “filter-tag” 且 Tag=TagA的消息,要是生产者发送的消息Tag=TagB,则该消费者不会消费这条消息

8.创建consumer8测试sql92消息过滤

@Component
@RocketMQMessageListener(
        topic = "filter-sql92",
        consumerGroup = "wolfcode-consumer1",
        selectorType = SelectorType.SQL92,
        selectorExpression = "age > 18"

)
public class Consumer8 implements RocketMQListener<String>{
    @Override
    public void onMessage(String s) {
        System.out.println(s);
    }
}

访问http://127.0.0.1:9090/sql92?msg=sql92123,发现消费者只会消费topic = “filter-sql92” 且 表达式中的age>18的消息,要是生产者发送的消息age=15,则该消费者不会消费这条消息