消息中间件-微服务(什么是消息中间件 消息中间件使用场景 常见消息中间件比较 RocketMQ RocketMQ的架构及概念 消息发送接受 使用场景 )

目录

消息中间件的产生的背景

一、什么是消息中间件

二、消息中间件使用场景

1. 异步处理

2. 应用解耦

三、常见消息中间件比较 

四、RocketMQ 

1. 环境准备 

2. 安装RocketMQ 

3. 启动RocketMQ

4. 安装可视化插件

五、RocketMQ的架构及概念

六、消息发送接受

1. 发送同步消息  

2. 接收消息 

3. 发送异步消息

4. 单向发送消息 

 5. 消费消息

七、使用场景 

1. 订单微服务发送消息  

2 用户微服务订阅消息

​编辑


消息中间件的产生的背景

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

一、什么是消息中间件

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

二、消息中间件使用场景

1. 异步处理

场景说明:用户注册后,需要发注册邮件和注册短信
传统的做法有两种
a.串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信,以上三个任务全部完成后,返回给客户端
b.并行方式:将注册信息写入数据库成功后,发送注册邮件的同时发送注册短信,以上三个任务完成后,返回给客户端;与串行的差别是并行的方式可以提高处理的时间
引入消息队列,改造后的架构如下
        按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50 毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此架构改变后,系统的吞吐量比串行提高了3 倍,比并行提高了 2

2. 应用解耦

场景说明:用户下单后,订单系统需要通知库存系统,传统的做法是订单系统调用库存系统的接口

解耦合后
订单系统:假如在下单时库存系统不能正常使用,也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了,实现订单系统与库存系统的应用解耦

三、常见消息中间件比较 

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

四、RocketMQ 

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

1. 环境准备 

下载 RocketMQ
http://rocketmq.apache.org/release_notes/release-notes-4.4.0/
环境要求
64 位操作系统
JDK 1.8+
安装 Maven

2. 安装RocketMQ 

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

3. 启动RocketMQ

切换到安装目录
rocketmqbin目录下
启动NameServer
start mqnamesrv.cmd

启动 Broker
start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true

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

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 个角色,分别是: NameServer Broker Producer

RocketMQ组件介绍

Broker(邮局,邮递员)

  • Broker是RocketMQ的核心,负责消息的接收、存储、投递等功能。

NameServer(各个邮局的管理机构)

  • 消息队列的协调者,Broker向其注册路由信息,同时Producer和Consumer向其获取路由信息。

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是消息的载体。

六、消息发送接受

导入坐标

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

1. 发送同步消息  

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

/**
 * @author zkt
 * @Version 1.0
 * @since 2024/7/30
 */
//发送同步消息
public class RocketMQSendTest1 {
    public static void main(String[] args) throws Exception {
        //1. 创建消息生产者, 指定生产者所属的组名
        DefaultMQProducer producer = new DefaultMQProducer("myproducer-group");
        //2. 指定Nameserver地址
        producer.setNamesrvAddr("127.0.0.1:9876");
        //3. 启动生产者
        producer.start();
        for (int i = 0;i<10;i++) {
            //4. 创建消息对象,指定主题、标签和消息体
            Message msg = new Message("myTopic", "myTag", ("还是想考研"+i).getBytes());
            //5. 发送消息
            SendResult sendResult = producer.send(msg);
            System.out.println(sendResult);
        }
        //6. 关闭生产者
        producer.shutdown();
    }
}

2. 接收消息 

消息接收步骤:
  • 1. 创建消息消费者, 指定消费者所属的组名
  • 2. 指定Nameserver地址
  • 3. 指定消费者订阅的主题和标签
  • 4. 设置回调函数,编写处理消息的方法
  • 5. 启动消息消费者

/**
 * @author zkt
 * @Version 1.0
 * @since 2024/7/30
 */
//接收消息
public class RocketMQReceiveTest1 {
    public static void main(String[] args) throws Exception {
        //1. 创建消息消费者, 指定消费者所属的组名
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("myconsumer-group");
        //2. 指定Nameserver地址
        consumer.setNamesrvAddr("127.0.0.1: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.");
    }
}

3. 发送异步消息

异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待 Broker 的响应。只会等待MQ 发送状态
//发送异步消息
//异步发送比较浪费性能,经常会失败,所以发送多几次并且让线程休眠几秒
public class RocketMQSendTest2 {
    public static void main(String[] args) throws Exception {
        //1. 创建消息生产者, 指定生产者所属的组名
        DefaultMQProducer producer = new DefaultMQProducer("myproducer-group");
        //2. 指定Nameserver地址
        producer.setNamesrvAddr("127.0.0.1:9876");
        //3. 启动生产者
        producer.start();
        for (int i = 0;i<10;i++){
            //4. 创建消息对象,指定主题、标签和消息体
            Message msg = new Message("myTopic", "myTag2", ("还是想考研").getBytes());
            //5. 发送消息
            producer.send(msg, new SendCallback() {
                        @Override
                        public void onSuccess(SendResult sendResult) {
                            System.out.println("发送成功:"+sendResult);
                        }
                        @Override
                        public void onException(Throwable e) {
                            System.out.println("发送异常:"+e);
                        }
                    }
            );
            TimeUnit.SECONDS.sleep(3);
        }
        //6. 关闭生产者
        producer.shutdown();
    }
}

4. 单向发送消息 

这种方式主要用在不特别关心发送结果的场景,例如日志发送。
//单向发送消息
public class RocketMQSendTest3 {
    public static void main(String[] args) throws Exception {
        //1. 创建消息生产者, 指定生产者所属的组名
        DefaultMQProducer producer = new DefaultMQProducer("myproducer-group");
        //2. 指定Nameserver地址
        producer.setNamesrvAddr("127.0.0.1:9876");
        //3. 启动生产者
        producer.start();
        for (int i = 0;i<10;i++){
            //4. 创建消息对象,指定主题、标签和消息体
            Message msg = new Message("myTopic", "myTag3", ("还是想考研").getBytes());
            //5. 发送消息
            // 发送单向消息,没有任何返回结果
            producer.sendOneway(msg);

            TimeUnit.SECONDS.sleep(3);
        }
        //6. 关闭生产者
        producer.shutdown();
    }
}

这里启动两个消费者接收 并且使用 负载均衡方式 轮询cluster

 5. 消费消息

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

七、使用场景 

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

1. 订单微服务发送消息  

在之前feign的代码上进行改写

1 在 shop-order服务 中添加rocketmq的依赖
        <!-- mq -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
        </dependency>

 父项目也要导 

2 添加配置

rocketmq:
  name-server: localhost:9876
  producer:
    group: shop-order

3 编写测试代码

修改原有的下单服务

@RestController
public class OrderController {

    @Autowired
    private IProductService productService;

    @Autowired
    private IOrderService orderService;


    @Autowired(required = false)
    private RocketMQTemplate rocketMQTemplate;

    //Ribbon下单
    @RequestMapping("/order/prod/{pid}")
    public Order order(@PathVariable("pid") Integer pid) {
        //通过fegin调用商品微服务
        Product product = productService.findByPid(pid);
        //下单(创建订单)
        Order order = new Order();
        order.setUid(1);
        order.setUsername("测试用户");
        order.setPid(pid);
        order.setPname(product.getPname());
        order.setPprice(product.getPprice());
        order.setNumber(1);
        orderService.createOrder(order);

        //下单成功之后,将消息放到mq中
        rocketMQTemplate.convertAndSend("order-topic", order);

        return order;
    }

2 用户微服务订阅消息

1 修改 shop-user 模块配置

        <!-- mq -->
        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
        </dependency>

2 修改置文件
rocketmq:
  name-server: localhost:9876

 

3 编写消息接收服务
/**
 * @author zkt
 * @Version 1.0
 * @since 2024/7/30
 */
//发送短信的服务
@Service
@RocketMQMessageListener(consumerGroup = "shop-user", topic = "order-topic")
public class SmsService implements RocketMQListener<Order> {

    @Override
    public void onMessage(Order order) {
        System.out.println("这是一条短信:"+order);
    }
}

4 启动服务,执行下单操作,观看后台输出 

下单成功

发送短信成功        

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

冯诺依曼转世

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

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

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

打赏作者

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

抵扣说明:

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

余额充值