RocketMQ入门篇-------整合Springboot

写在前面

从安装到测试成功,花了好几天的时间。在网上也查了很多的资料。但是很多时候都是徒劳的。第一次学习MQ,想把这些天学到的东西总结一下。也很欢迎各位大神、大牛来评论。
首先,我们花点时间介绍一下RocketMQ的性能及一些角色。个人觉得这是很有必要的。其实,不管是什么MQ,他们都是MQ,只要是MQ,他都具有一下特点:

  • 削峰填谷(主要解决瞬时写压力大于应用服务能力导致消息丢失、系统奔溃等问题)
  • 系统解耦(解决不同重要程度、不同能力级别系统之间依赖导致一死全死)
  • 提升性能(当存在一对多调用时,可以发一条消息给消息系统,让消息系统通知相关系统)
  • 蓄流压测(线上有些链路不好压测,可以通过堆积一定量消息再放开来压测)
    而RocketMQ独有的特性有:
  • 支持结合rocketmq的多个系统之间数据最终一致性(多方事务,二方事务是前提)
  • 支持18个级别的延迟消息(rabbitmq和kafka不支持)
  • 支持指定次数和时间间隔的失败消息重发(kafka不支持,rabbitmq需要手动确认)
  • 支持consumer端tag过滤,减少不必要的网络传输(rabbitmq和kafka不支持)
  • 支持重复消费(rabbitmq不支持,kafka支持)
RocketMQ整体结构及特色

在这里插入图片描述
如上图所示,整体可以分成4个角色,分别是:Producer,Consumer,Broker以及NameServer;

角色如下:
NameServer

可以理解为是消息队列的协调者,Broker向它注册路由信息,同时Client向其获取路由信息,如果使用过Zookeeper,就比较容易理解了,但是功能比Zookeeper弱;
NameServer本身是没有状态的,并且多个NameServer直接并没有通信,可以横向扩展多台,Broker会和每一台NameServer建立长连接;

Broker

Broker是RocketMQ的核心,提供了消息的接收,存储,拉取等功能,一般都需要保证Broker的高可用,所以会配置Broker Slave,当Master挂掉之后,Consumer然后可以消费Slave;
Broker分为Master和Slave,一个Master可以对应多个Slave,Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave;

Producer

息队列的生产者,需要与NameServer建立连接,从NameServer获取Topic路由信息,并向提供Topic服务的Broker Master建立连接;Producer无状态.

Consumer

消息队列的消费者,同样与NameServer建立连接,从NameServer获取Topic路由信息,并向提供Topic服务的Broker Master,Slave建立连接;

Topic 和 Message Queue

在介绍完以上4个角色以后,还需要重点介绍一下上面提到的Topic和Message Queue;字面意思就是主题,用来区分不同类型的消息,发送和接收消息前都需要先创建Topic,针对Topic来发送和接收消息,为了提高性能和吞吐量,引入了Message Queue,一个Topic可以设置一个或多个Message Queue,有点类似kafka的分区(Partition),这样消息就可以并行往各个Message Queue发送消息,消费者也可以并行的从多个Message Queue读取消息;

跟其他的MQ相比,可能角色多了点,但是没有关系。这里我说明一点。前面两种角色不需要我们自己创建,开发者只需要自己创建后三者角色。
在申明一点:我们一定要提前搭建好RocketMQ的基本环境,如果你还不会搭建的,请点击这里;如果你搭建好了,先启动NameServer服务器,在启动Broker服务器,在运行相应的jar包。详细步骤上一篇文章都有。

整合RocketMQ基本上算是有两种方法
第一种 springboot提供了依赖

我们先看怎么整合的问题,至于问题,后面再说

Pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.tff</groupId>
    <artifactId>springboot_rocketmq</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/>
    </parent>
    <dependencies>

        <!-- springboot 整合RocketMQ依赖 -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.0.3</version>
        </dependency>

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

生产者角:

package com.tff.springbootrocketmq;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Producer{
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    
    public void sendMessage(){
        //参数1:topic  参数2:要发送的消息
        rocketMQTemplate.convertAndSend("hoppy","i love code");
        System.out.println("消息发送完成");
    }
}

消费者角色:

package com.tff.springbootrocketmq;

import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;

#这个注解相当重要  参数1 topic  参数2  消息的组
@RocketMQMessageListener(topic = "hoppy",consumerGroup = "${rocketmq.producer.group}")
@Component
public class Consumer implements RocketMQListener<String> {
    @Override
    public void onMessage(String s) {
        System.out.println("接收的消息:"+s);
    }
}

controller层

package com.tff.web;

import com.tff.springbootrocketmq.Producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.UnsupportedEncodingException;

@RestController
public class RocketMQController {

    @Autowired
    private Producer producer;  //采用springboot模板集成方式来整合

    @RequestMapping("/push")
    public String pushMsg() throws UnsupportedEncodingException {
        producer.sendMessage();
        return "ERROR";
    }
}

大家可以不写这一层,直接利用springboot的测试单元也可以
配置文件

# 安装RocketMQ的主机IP地址和端口
rocketmq.name-server=127.0.0.1:9876 
#发送消息的组
rocketmq.producer.group=group1

测试:
浏览器地址栏输入:http://localhost:8080/push
在这里插入图片描述
我们可以看到,消息消费端已经接受到消息了。

打开RocketMQ的图形用户界面
在这里插入图片描述
我们看到,已经可以看到我们的生产者了。
我们在来看一下“消息”菜单
在这里插入图片描述
这些都是我们刚才发送的消息

这种方式有一定的局限性,Springboot在这方面做的不是很好。所以,我们在开发过程中经常使用第二种方式,开发者自己进行封装。

原始的API开发方式

Pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tff</groupId>
    <artifactId>rocketmq_test_demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-client</artifactId>
            <version>4.3.1</version>
        </dependency>
    </dependencies>
</project>

生产者:

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;
public class Producer1 {
    public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException, UnsupportedEncodingException {
        //创建默认 DefaultMQProducer
        DefaultMQProducer producer = new DefaultMQProducer("demo1");
        //设置nameServer地址
        producer.setNamesrvAddr("127.0.0.1:9876");
        //开启 DefaultMQProducer
        producer.start();
        //创建消息
        Message message = new Message("Hello_World","jsj","二叉树、平衡二叉树、图、有向图、无向图、线性结构等".getBytes(RemotingHelper.DEFAULT_CHARSET));
        //发送消息
        SendResult result = producer.send(message);
        System.out.println(result);
        //关闭
        producer.shutdown();
    }
}

消息消费者

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 org.apache.rocketmq.remoting.common.RemotingHelper;

import java.io.UnsupportedEncodingException;
import java.util.List;

public class Consumer1 {
    public static void main(String[] args) throws MQClientException {

        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("demo1");
        consumer.setNamesrvAddr("127.0.0.1:9876");
        //从那个主题中获取  第二个参数是怎么过滤,*代表所有,也可以是tag
        consumer.subscribe("Hello_World","*");
        //创建消息监听器
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                //迭代消息
                for(MessageExt msg : list){
                    try {
                        //获取主题
                        String topic = msg.getTopic();
                        //获取tag
                        String tag = msg.getTags();
                        //获取消息
                        byte[] mes = msg.getBody();
                        String message = new String(mes,RemotingHelper.DEFAULT_CHARSET);
                        System.out.println("接收到消息==: 消息主题:"+topic+", tag: "+tag+", 内容:"+message);
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                        //消息接收失败,重试机制
                        return ConsumeConcurrentlyStatus.RECONSUME_LATER;
                    }
                }
                //消息接收成功,返回状态
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //开启consumer
        consumer.start();
    }

}

这里就不具体解释代码的含义了,都有注释
这里只需要补充一点;也是特别重要的一点。
就是我们下载的RocketMQ的版本要是我们导入的依赖版本一致,否则,会出现bug
在这里插入图片描述例如,我这个是4.3.1版本
那么,我的maven依赖也因该是这个版本

测试:
先启动生产者:
在这里插入图片描述
已经发送成功

再启动消费者:
在这里插入图片描述
基本上就是这两种方式了,一般开发过程中,选用后者的比较多。大家在开发过程中,可以自己封住,这里只是为了测试,我没有进行封装。

另外,附一下RocketMQ的官网地址;很有用的,因为是阿里开发的,所以中国人看起来还是挺可以的。
官网地址:http://rocketmq.apache.org/docs/simple-example/

欢迎大家批评指正!如果你喜欢的话,请留下您宝贵的建议……

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值