RocketMQ分布式消息队列

什么是MQ:

        MQ全称为Message Queue,即消息队列.是一种提供消息队列服务的中间件,也称为消息中间件,是一套提供了消息生 产、存储、消费全过程的软件系统,遵循FIFO(先进先出,后进后出)原则。

MQ作用:

        在一些特点的场景中,我们需要执行两个步骤,比如地铁,我们要先刷码出站,过几分钟之后才会扣款,上下班高峰期使用天府通刷码的人非常多,意味并发量很高,一个出站请求到后台需要做费用计算,费用结算。由于并发很高,并且费用结算和积分等业务本来就耗时,况且支付服务也不一定能承担那么大的请求量.如果我们等到扣款之后才能通行,这会使用户等待一段时间,后续的人也要刷码出站,会导致地铁站堆集大量人员.

        当服务器线程耗尽,后续请求会等待变慢,再加上高并发请求就会导致后续请求越来越慢,请求长时间等待,导致大量请求超时。并发太高,可能会导致服务器的内存上升,CPU使用率急速上升,甚至导致服务器宕掉。

         我们可以使用MQ消峰,用户刷码直接返回结果出站,MQ记录数据,稍后再通过MQ计算费用,结算费用,这样使得用户体验度大幅提高

MQ的使用场景(重要)

  • 限流削峰

    MQ可以将系统的超量请求暂存其中,以便系统后期可以慢慢进行处理,从而避免了请求的丢失或系统 被压垮。

  • 异步&解耦

    上游系统对下游系统的调用若为同步调用,则会大大降低系统的吞吐量与并发度,且系统耦合度太高。 而异步调用则会解决这些问题。所以两层之间若要实现由同步到异步的转化,一般性做法就是,在这两层间添加一个MQ层。 即使消费者挂掉也不影响生产者工作,只要把消息放入队列即可,消费者重启后自己消费即可。

  • 数据收集

    分布式系统会产生海量级数据流,如:业务日志、监控数据、用户行为等。针对这些数据流进行实时或 批量采集汇总,然后对这些数据流进行大数据分析,这是当前互联网平台的必备技术。通过MQ完成此 类数据收集是最好的选择。

  • 大数据处理

    比如我们的平台向“三方平台”获取数据,一次请求了大量数据回来要进行处理,由于数据较多处理不过来,那么就可以放入MQ,再创建一些消费者进行数据处理即可。

如下情况不太适合MQ

  • 小项目,体量不大,并发量低的使用MQ会太过笨重 - 你可以考虑使用Redis做一个消息队列。

  • 对数据的一致性有要求(强一致性)的的场景不适合使用MQ,因为MQ是异步的。

MQ的优点:

        1.提高系统响应速度      

                因为MQ是采用异步处理,不需要像同步一样等待线程处理完之后才能返回,降低了数据的响应时间

        2.提高系统稳定性

                降低了峰值,减少了高并发的风险,提高了系统的稳定性.即便是系统挂掉了也没问题,操作内容放到消息队列不丢失,后续重新消费者一样能消费做业务处理

        3.采用FIFO

                先进先出,能够保证消息按照添加的数据被消费。

MQ常见协议

  • AMQP协议 AMQP是一套公开的消息队列协议,最早在2003年被提出,它旨在从协议层定义消息通信数据的标准格式, 为的就是解决MQ市场上协议不统一的问题。基于此协议的客户端与消息中间件可传递 消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制,RabbitMQ就是遵循AMQP标准协议开发的MQ服务。 官方:Home | AMQP

  • JMS协议 JMS是Java消息服务,是java提供的一套消息服务API标准,其目的是为所有的java应用程序提供统一的消息通信的标准,类似java的 jdbc,只要遵循jms标准的应用程序之间都可以进行消息通信。它和AMQP有什么 不同,jms是java语言专属的消 息服务标准,它是在api层定义标准,并且只能用于java应用;而AMQP是在协议层定义的标准,是跨语言的 。

  • MQTT MQTT,Message Queuing Telemetry Transport(消息队列遥测传输),是IBM开发的一个即时通讯协 议,是一种二进制协议,主要用于服务器和低功耗IoT(物联网)设备间的通信。该协议支持所有平 台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器的通信协议。 RabbitMQ通 过插件可以支持该协议。

RocketMQ介绍

RocketMQ是什么    

        RocketMQ是一个统一消息引擎、轻量级数据处理平台。

        RocketMQ是⼀款阿⾥巴巴开源的消息中间件,双十一承载了万亿级消息的流转,2016年11⽉,阿⾥巴巴向 Apache 软件基⾦会捐赠 RocketMQ,成为 Apache 孵化项⽬,2017 年 9 ⽉ ,Apache 宣布 RocketMQ孵化成为 Apache 顶级项⽬(TLP )成为国内⾸个互联⽹中间件在 Apache 上的顶级项⽬。

RocketMQ特征(了解)

  • 支持集群模型、负载均衡、水平扩展能力

  • 亿级别消息堆积能力

  • 采用零拷贝的原理,顺序写盘,随机读

  • 底层通信框架采用Netty NIO

  • NameServer代替Zookeeper,实现服务寻址和服务协调

  • 消息失败重试机制、消息可查询

  • 强调集群无单点,可扩展,任意一点高可用,水平可扩展

  • 经过多次双十一的考验

 RocketMQ的集群架构如下

         RocketMQ主要由 Producer、Broker、Consumer、NameServer 四部分组成,其中Producer 负责生产消息,Consumer 负责消费消息,Broker 负责存储消息,NameServer 负责管理Broker的地址。Broker 在实际部署过程中对应一台服务器。

RocketMQ工作原理

如何使用RocketMQ:

        第一步:下载RocketMQ,配置环境变量

下载地址:http://rocketmq.apache.org/release_notes/release-notes-4.2.0/

        第二步:导入依赖

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

        第三步:创建生产者,消费者

public class Producer {

    public static void main(String[] args) throws Exception {

        //1.创建生产者组
        DefaultMQProducer producer = new DefaultMQProducer("producer-hello");
        //2.设置NameServer地址
        producer.setNamesrvAddr("127.0.0.1:9876");
        //3.启动producer实例
        producer.start();
        //4.创建消息
        Message message = new Message("log-topic", "info-tag", "这是一个info信息".getBytes(RemotingHelper.DEFAULT_CHARSET));
        //5.发送消息
        SendResult result = producer.send(message);
        //6.关闭producer实例
        System.out.println("发送完毕,结果: "+result);
    }
}
  • DefaultMQProducer : MQ生产者 , 可以指定组名 producerGroupName

  • producer.setNamesrvAddr : 指定Name Server地址,用作Brocker发现。注意IP和启动name server服务时指定的IP保持一致。

  • producer.start() : 启动生产者

  • new Message("topic_log","tags_error",("我是消息"+i).getBytes()) :消息,参数为:topic,tags,内容

    • producer.send(message) : 发送消息

    • SendResult :发送结果,其中包含

    • sendStatus=SEND_OK :发送状态

    • msgId :producer 创建的消息ID

    • offsetMsgId :Brocker创建的消息ID

    • messageQueue :消息存储的队列

    • producer.shutdown():关闭生产者

public class Consumer {
    public static void main(String[] args) throws MQClientException {
        //1.创建消费者组
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer-hello");
        //2.设置NameServer地址
        consumer.setNamesrvAddr("127.0.0.1:9876");
        //3.订阅topic,指定tag标签
        consumer.subscribe("log-topic","info-tag");
         //4.注册消息监听器
        consumer.registerMessageListener(new MessageListenerConcurrently(){

            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                System.out.printf("%s 接收到新的消息:  %n", Thread.currentThread().getName());
                msgs.stream().forEach(messageExt -> {
                    String body = null;
                    try {
                        body = new String(messageExt.getBody(), RemotingHelper.DEFAULT_CHARSET);
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    System.out.println(body);
                });
			   //失败消费,稍后尝试消费,会进行多次重试
                //return ConsumeConcurrentlyStatus.RECONSUME_LATER;
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        //5.启动消费者
        consumer.start();
        System.out.println("消费者启动...");

    }
}
  • DefaultMQPushConsumer :消费者 , 可以指定 consumerGroupName

  • consumer.setNamesrvAddr : 设置name server 地址

  • consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET) :从什么位置开始消费

  • consumer.subscribe("topic_log", "tags_error") :订阅某个topic下的某个tags的消息

  • consumer.registerMessageListener :注册消息监听器,拿到消息后,进行消息处理。

  • ConsumeConcurrentlyStatus :消费者消费结果状态,ConsumeConcurrentlyStatus.CONSUME_SUCCESS代表成功,ConsumeConcurrentlyStatus.RECONSUME_LATER代表消费失败,稍后重试,会进行多次重试

RocketMQ 核心概念

Producer 生产者

        RocketMQ提供多种发送方式,同步发送、异步发送、顺序发送、单向发送。同步和异步方式均需要Broker返回确认信息,单向发送不需要。 Producer会使用一定的算法选择把消息发送到哪个master的某个queue中.

Consumer 消费者

        Consumer 支持两种消费形式:拉取式消费、推动式消费。(主动,被动),RocketMQ中的消息消费者都是以消费者组(Consumer Group)的形式出现的。消费者组是同一类消费者的集合,这类Consumer消费的是同一个Topic类型的消息,不同的 Consumer Group可以消费同一个Topic。 一个Consumer Group内的Consumer可以消费多个Topic的消息。

Topic 消息主题

   Topic表示一类消息的集合,每个topic主题包含若干条message消息,每条message消息只能属于一个topic主题,Topic是RocketMQ进行消息订阅的基本单位。

Message 消息

        消息是指,消息系统所传输信息的物理载体,生产和消费数据的最小单位,每条消息必须属于一个主题。

Tag 标签

        为消息设置的标志,用于同一主题下区分不同类型的消息.Topic是消息的一级分类,Tag是消息的二级分类

MessageQueue队列

        一个Topic中可以包含多个Queue,一 个Topic的Queue也被称为一个Topic中消息的分区(Partition)。 在一个Consumer Group内,一个Queue最多只能分配给一个Consumer,一个Cosumer可以分配得到多个Queue。这样的分配规则,每个Queue只有一个消费者,可以避免消费过程中的多线程处理和资源锁定,有效提高各Consumer消费的并行度和处理效率。

SpringBoot整合RocketMQ

        1.导入依赖

<parent>
    <groupId> org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-spring-boot-starter</artifactId>
       <!-- <version>2.0.4</version> -->
         <version>2.2.1</version>
    </dependency>
 
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

        2.配置启动类

@SpringBootApplication
public class ApplicationStart {
    public static void main(String[] args) {
        SpringApplication.run(ApplicationStart.class);
    }
}

        3.配置文件

rocketmq:
  name-server: 127.0.0.1:9876
  #生产者配置
  producer:
    #生产者组名字
    group: "service-producer"
    # 消息最大长度 默认 1024 * 1024 * 4 (4M)
    max-message-size: 4194304
    # 发送消息超时时间,默认 3000
    send-message-timeout: 3000
    # 发送消息失败重试次数,默认2
    retry-times-when-send-failed: 2
    # 异步消息发送失败重试次数
    retry-times-when-send-async-failed: 2
    #达到 4096 ,进行消息压缩
    compress-message-body-threshold: 4096
  consumer:
    #消费者名字
    group: "service-consumer"
    #批量拉取消息数量
    pull-batch-size: 10
    message-model: CLUSTERING
    selector-expression: "*"

同步发送消息

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    // 同步发送信息
    @RequestMapping("/order/{msg}")
    public String senMessage(@PathVariable("msg") String msg) {
        // 构建消息对象
        Message<String> message = MessageBuilder.withPayload(msg).build();
        rocketMQTemplate.send("topic-order:tags-order-pay", message);
        //发送同步消息,2s发送不成功就超时
//        SendResult sendResult = rocketMQTemplate.syncSend("topic-order:tags-order-pay", message, 2000);
        return "消息发送成功";
    }

异步发送消息(包含延迟消息)

  @Autowired
    private RocketMQTemplate rocketMQTemplate;    
// 异步发送信息
    @RequestMapping("/order/async/{msg}")
    public String sendAsyncMessage(@PathVariable("msg") String msg) {
        // 创建消息对象,发送消息需要消息对象
        Message<String> message = MessageBuilder.withPayload(msg).build();
        // 发送延迟消息
        SendResult sendResult = rocketMQTemplate.syncSend("topic-order:tags-order-pay", message, 2000, 3);//延迟消息
        // 异步发送消息
//        rocketMQTemplate.asyncSend("topic-order:tags-order-pay", message, new SendCallback() {
//            @Override
//            public void onSuccess(SendResult sendResult) {
//                System.out.println(sendResult);
//                System.out.println("发送成功");
//            }
//
//            @Override
//            public void onException(Throwable e) {
//                System.out.println("发送失败");
//                e.printStackTrace();
//            }
//        });
        return "发出去了";
    }

我这里 指定了发送的主题(topic)和标签(tags),固定语法("topic:tags")

2000是指等待时间超过2000毫秒是超时等待

3是消息的延迟等级也就是10秒

发送延迟消息是有一个等级来定义延迟时间的,一共有19个等级,分别对于以下时间

messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h 2d 这个时候总共就有19个level

发送事务消息

     如果业务只涉及到一个数据库的写操作,我们只需要保证这一个事物的提交和回滚,这种事务管理叫传统事物或本地事务,如果业务涉及到多个数据库(多个服务)的写操作,我们需要保证多个数据库同时提交或回滚,这种跨多个数据库的事务操作叫分布式事务。

   使用RocketMQ发送事务消息需要用到分布式事

  1. 编写本地事务检查监听TransactionListener ,一是执行本地事务逻辑,二是返回本地事务执行状态

  2. 发消息时生产者需要设置producer.setTransactionListener 事务监听

 创建事务监听器

// 发送消息事务监听器,监听本地事务是否执行成功,如果成功就表示消息可以被消费,如果失败,则删除MQ中的信息,确保事务消息一致性
@RocketMQTransactionListener(txProducerGroup = "order-tx-listener") // 声明当前类是事务监听器类,txProducerGroup是该事务监听器的名字
@Slf4j
public class CourserOrderTransactionListener implements RocketMQLocalTransactionListener {

    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(org.springframework.messaging.Message message, Object o) {
        byte[] messageBody = (byte[])message.getPayload();
        String msg = new String(messageBody);
        log.info("打印一下表示持久化成功{}",msg);   // 这里应该做持久化操作,如果持久化成功就返回COMMIT,失败就返回ROLLBACK
        return RocketMQLocalTransactionState.COMMIT;
    }

    // 这里表示不知道消息返回是否成功或者失败,走检查路线
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(org.springframework.messaging.Message message) {
        byte[] messageExtBody = (byte[])message.getPayload();
        String msg = new String(messageExtBody);
        log.info("打印一下表示持久化成功{}",msg);
        return RocketMQLocalTransactionState.COMMIT;
    }
}

发送事务消息

   // 事务消息
    @RequestMapping("/order/tx/{msg}")
    public String sendTransactionMessage(@PathVariable("msg") String msg){
        // 创建消息对象,发送消息需要消息对象
        Message<String> message = MessageBuilder.withPayload(msg).build();
        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction(
                "order-tx-listener",    // 事务监听器的名字
                "topic-order:tags-order-pay",   // 目的地,发送到哪个主题下的哪个标签
                message,       // 发送的信息
                "arg");//扩展参数 Object;  该参数会传递给事务监听器的arg
        LocalTransactionState localTransactionState = transactionSendResult.getLocalTransactionState();
        SendStatus sendStatus = transactionSendResult.getSendStatus();
        log.info("本地事务状态:{}, 消息发送状态:{}",localTransactionState,sendStatus);
        return "OK";
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值