消息中间件-RocketMQ

消息中间件产生的背景

        在网络通讯中,Http 请求默认采用同步请求方式,基于请求与响应模式,在客户端与服务器进行通讯时,客户端调用服务端接口后,必须等待服务端完成处理后返回结果给客户端才能继续执行,这种情况属于同步调用方式。如果服务器端发生网络延迟、不可达的情况,可能客户端也会受到影响。

一、什么是消息中间件

        消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行 分布式系统 的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。对于消息中间件,常见的角色大致也就有Producer (生产者)、 Consumer (消费者)例如:寄快递。
 

 

二、消息中间件的使用场景 

2.1 异步处理

场景说明:用户注册后,需要发注册邮件和注册短信
传统的做法有两种:
1、串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信,以上三个任务全部完成后,返回给客户端
2、并行方式:将注册信息写入数据库成功后,发送注册邮件的同时发送注册短信,以上三个任务完成后,返回给客户端;与串行的差别是并行的方式可以提高处理的时间

引入消息队列,改造后的架构如下

按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是 50 毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此架构改变后,系统的吞吐量比串行提高了3 倍,比并行提高了 2

2.2 应用解耦

场景说明:用户下单后,订单系统需要通知库存系统,传统的做法是订单系统调用库存系统的接口
解耦合后:
订单系统:假如在下单时库存系统不能正常使用,也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了,实现订单系统与库存系统的应用解耦。

三、常见的消息中间件比较

特性MQ
ActiveMQ
RabbitMQ
RocketMQ
Kafka
生产者消费者模式
支持
支持
支持
支持
发布订阅模式
支持
支持
支持
支持
请求回应模式
支持
支持
不支持
不支持
Api 完备性
多语言支持
支持
支持
Java
支持
单机吞吐量
万级
万级
万级
十万级
消息延迟
微秒级毫秒级毫秒级
可用性
高(主从)
高(主从)
非常高(分布式)
非常高(分布式)
消息丢失
理论上不会丢失
理论上不会丢失
文档的完备性
较高
提供快速入门
社区活跃度
商业支持
商业云商业云

四、RocketMQ

RocketMQ 是阿里巴巴开源的分布式消息中间件,现在是 Apache 的一个顶级项目。在阿里内部使用非常广泛,已经经过了" 11" 这种万亿级的应用场景考验。

4.1 环境准备

下载RocketMQ:http://rocketmq.apache.org/release_notes/release-notes-4.4.0/

环境需要:

  • 64位操作系统
  • JDK 1.8+
  • 安装Maven

4.2 安装RocketMQ

  1. 解压缩安装包
  2. 配置环境变量
  3. 变量名:ROCKETMQ_HOME 变量值:MQ解压缩路径
  4. 编辑: path %ROCKETMQ_HOME%\bin

4.3 启动RocketMQ

  • 切换到安装目录
    • rocketmq的bin目录下
  • 启动NameServer
    • start mqnamesrv.cmd
  • 启动Broker
    • start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true
    • 如果弹出框提示‘错误: 找不到或无法加载主类 xxxxxx’。在bin下找到并打开runbroker.cmd,然后将‘%CLASSPATH%’加上英文双引号

 4.4 安装可视化插件

  • github下载
    • rocketmq-externals-rocketmq-console-1.0.0.zip
  • 解压压缩包
  • 进入\rocketmq-console\src\main\resources文件加,用编辑器打开application.properties文件
  • 进入rocketmq-externals\rocketmq-console 文件夹,执行:mvn clean package - Dmaven.test.skip=true,编译生成jar
  • 编译成功后,在rocketmq-externals\rocketmq-console下会生成target文件夹进入target后执行:java -jar rocketmq-console-ng-1.0.0.jar,这里是在启动jar工程。
  • 启动完毕后,在浏览器输入:http://localhost:8085/ 进入控制台

五、RocketMQ的架构及概念

 如上图所示,整体可以分成4个角色,分别是:NameServerBrokerProducerConsumer

  • Broker(邮递员)
    • BrokerRocketMQ的核心,负责消息的接收,存储,投递等功能
  • NameServer(邮局)
    • 消息队列的协调者,Broker向它注册路由信息,同时ProducerConsumer向其获取路由信息
  • Producer(寄件人)
    • 消息的生产者,需要从NameServer获取Broker信息,然后与Broker建立连接,向Broker发送消息
  • Consumer(收件人)
    • 消息的消费者,需要从NameServer获取Broker信息,然后与Broker建立连接,从Broker获取消息
  • Topic(地区)
    • 用来区分不同类型的消息,发送和接收消息前都需要先创建Topic,针对Topic来发送和接收消息
  • Message Queue(邮件)
    • 为了提高性能和吞吐量,引入了Message Queue,一个Topic可以设置一个或多个Message Queue,这样消息就可以并行往各个Message Queue发送消息,消费者也可以并行的从多个 Message Queue读取消息
  • Message
    • Message 是消息的载体

六、消息发送和接收演示

首先需要导入RocketMQ的坐标

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>4.4.0</version>
</dependency>

6.1 发送同步消息

这种可靠性同步地发送方式使用的比较广泛,比如:重要的消息通知,短信通知。
消息发送步骤 :
  1. 创建消息生产者, 指定生产者所属的组名
  2. 指定Nameserver地址
  3. 启动生产者
  4. 创建消息对象,指定主题、标签和消息体
  5. 发送消息
  6. 关闭生产者

6.1.1 发送同步消息

/**
 * @author lizhihui
 * @version 1.0
 * @since 2023/8/14
 */
// 发送同步消息
public class RocketMQSendTest1 {
    public static void main(String[] args) throws Exception {
        //1. 创建消息生产者, 指定生产者所属的组名
        DefaultMQProducer producer = new DefaultMQProducer("myproducer-group");
        //2. 指定Nameserver地址
        producer.setNamesrvAddr("localhost:9876");
        //3. 启动生产者
        producer.start();
        for (int i  = 0;i < 10;i++) {
            //4. 创建消息对象,指定主题、标签和消息体
            Message message = new Message("myTopic","myTag",("十行代码九个错误八个警告竟敢说七日精通六天学会五湖四海也不见如此三心二意之程序简直一等下流"+i).getBytes());
            //5. 发送消息
            SendResult sendResult = producer.send(message);
            System.out.println(sendResult);
        }
        //6. 关闭生产者
        producer.shutdown();
    }
}

6.1.2 发送异步消息

异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待 Broker 的响应。
/**
 * @author lizhihui
 * @version 1.0
 * @since 2023/8/14
 */
// 发送异步消息
public class RocketMQSendTest2 {
    public static void main(String[] args) throws Exception{
        DefaultMQProducer producer = new DefaultMQProducer("myproducer-group");
        producer.setNamesrvAddr("localhost:9876");
        producer.start();
        for (int i = 0;i < 10;i++) {
            Message message = new Message("myTopic","myTag2",("十行代码九个错误八个警告竟敢说七日精通六天学会五湖四海也不见如此三心二意之程序简直一等下流"+i).getBytes());
            producer.send(message, new SendCallback() {
                @Override
                public void onSuccess(SendResult sendResult) {
                    System.out.println("发送成功:" + sendResult);
                }
                @Override
                public void onException(Throwable throwable) {
                    System.out.println("发送失败" + throwable);
                }
            });
            TimeUnit.SECONDS.sleep(2);
        }
        producer.shutdown();
    }
}

6.1.3 单向发送消息

这种方式主要用在不特别关心发送结果的场景,例如日志发送。
// 单向发送
public class RocketMQSendTest3 {
    public static void main(String[] args) throws Exception{
        DefaultMQProducer producer = new DefaultMQProducer("myproducer-group");

        producer.setNamesrvAddr("localhost:9876");
        producer.start();
        for (int i = 0;i < 10;i++) {
            Message message = new Message("myTopic","myTag3",("十行代码九个错误八个警告竟敢说七日精通六天学会五湖四海也不见如此三心二意之程序简直一等下流"+i).getBytes());

            producer.sendOneway(message);
            TimeUnit.SECONDS.sleep(2);
        }
        producer.shutdown();
    }
}

6.2 接收消息

消息接收步骤 :
  1. 创建消息消费者, 指定消费者所属的组名
  2. 指定Nameserver地址
  3. 指定消费者订阅的主题和标签
  4. 设置回调函数,编写处理消息的方法
  5. 启动消息消费者
/**
 * @author lizhihui
 * @version 1.0
 * @since 2023/8/14
 */
public class RocketMQReceiveTest1 {
    public static void main(String[] args) throws Exception{
        //1. 创建消息消费者, 指定消费者所属的组名
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("myconsumer_group");
        //2. 指定Nameserver地址
        consumer.setNamesrvAddr("localhost:9876");
        //3. 指定消费者订阅的主题和标签
        consumer.subscribe("myTopic","*");
        //CLUSTERING-clustering  负载均衡模式  默认
        //BROADCASTING-broadcasting 广播
        consumer.setMessageModel(MessageModel.BROADCASTING);
        //4. 设置回调函数,编写处理消息的方法
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                System.out.println("获取到的消费数据:" + list);
                System.out.println(new String(list.get(0).getBody()));
                //返回消费状态
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //5. 启动消息消费者
        consumer.start();
        System.out.println("Consumer starting");
    }
}

6.3 消费消息

1、负载均衡模式(默认方式)

消费者采用负载均衡方式消费消息,多个消费者共同消费队列消息,每个消费者处理的消息不同
2、广播模式
消费者采用广播的方式消费消息,每个消费者消费的消息都是相同的
//广播模式消费
consumer.setMessageModel(MessageModel.BROADCASTING);
发送方式发送TPS发送结果反馈可靠性
同步发送不丢失
异步发送可能丢失
单向发送最快可能丢失

七、使用场景

接下来我们模拟一种场景 : 下单成功之后,向下单用户发送短信

7.1 订单微服务发送消息 

shop-order 服务 中添加 rocketmq 的依赖
<!--rocketmq-->
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>2.0.2</version>
</dependency>
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>4.4.0</version>
</dependency>

添加配置

rocketmq:
    name-server: 127.0.0.1:9876 #rocketMQ服务的地址
    producer:
        group: shop-order #生产者组

编写测试代码

**
 * @author lizhihui
 * @version 1.0
 * @since 2023/8/14
 */
@RestController
public class OrderController {

    @Autowired(required = false)
    private OrderMapper mapper;

    @Autowired(required = false)
    private RocketMQTemplate rocketMQTemplate;

    @RequestMapping("/order/prod/{pid}")
    public Order order(@PathVariable("pid") Integer pid){
        //下单(创建订单)
        Order order = new Order();
        order.setUid(1);
        order.setUsername("测试用户");
        order.setPid(pid);
        order.setPname("大豫竹");
        order.setPprice(2.0);
        order.setNumber(1);

//        mapper.insert(order);

        rocketMQTemplate.convertAndSend("order-topic",order);

        return order;
    }
}

7.2 用户微服务订阅消息

编写消息接收服务
/**
 * @author lizhihui
 * @version 1.0
 * @since 2023/8/14
 */
@Service
@RocketMQMessageListener(consumerGroup = "shop-user",topic = "order-topic")
public class SmsService implements RocketMQListener<Order> {
    @Override
    public void onMessage(Order order) {
        System.out.println(order);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农叮叮车

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值