RocketMQ

阿里巴巴开源的消息中间件框架,后捐献给apache基金会
RocketMQ特点:
1.能够保证严格的消息顺序
2.提供丰富的消息拉取模式
3.高效的订阅者水平扩展能力
4.实时的消息订阅机制
5.支持事务消息
6.亿级消息堆积能力

RocketMQ架构:
Name Server
Broker Master
Producer
Comsumer
Topic
Message Queue

在这里插入图片描述

BrokerName来决定主丛节点是否为一组,BrokerId为0表示这个服务是主节点 非0表示从节点
Broker主节点负责将消息者发送的消息写入,再将消息同步到从节点,消费端监听从节点接收消息

集群模式:
1.单Master模式
一旦Master宕机,整个服务不可用,线上不用,开发测试可以用
2.多Master模式
其中一个Master宕机其他Master也可以顶上,性能较高
3.多Master多Slave模式(异步)
一个Slave只有一个Master,一个Master可以用多个Slave,Master和Slave的消息复制使用异步
4.多Master多Slave模式(同步)
一个Slave只有一个Master,一个Master可以用多个Slave,Master和Slave的消息复制使用同步

集群架构使用双主双从 2m-2s

整合springboot依赖

   <!-- RocketMQ -->
<dependency>
    <groupId>com.alibaba.rocketmq</groupId>
    <artifactId>rocketmq-client</artifactId>
    <version>3.5.8</version>
</dependency>
<dependency>
    <groupId>com.alibaba.rocketmq</groupId>
    <artifactId>rocketmq-all</artifactId>
    <version>3.5.8</version>
    <type>pom</type>
</dependency>
RockerMQ可以发送普通消息、顺序消息、事务消息;

2种常见的消费消息模式:
    1.    DefaultMQPushConsumer
            消息消费者和broker建立长连接,broker每5秒检查一次队列有没这个消费者需要的消息,如果有就将消息推送给这个消息者
    2.    DefaultMQPullConsumer
            消息消费者直接从broker中拉取需要的消息

发送消息步骤:
    1.创建DefaultMQProducer
    2.设置NameServer地址
    3.开启DefaultMQProducer
    4.创建消息Message
    5.发送消息
    6.关闭DefaultMQProducer

消息发送测试:
public class TestProducer {
    public static void main(String[] args) {
        //创建MQ生产者对象
        DefaultMQProducer producer = new DefaultMQProducer("group1");
        try {
            //指定NameService地址
            producer.setNamesrvAddr("127.0.0.1:9876");
            //开启消息生产者
            producer.start();
            //创建消息对象
            Message message = new Message("base","tag1","Hello RocketMQ33".getBytes());
            //发送消息
            SendResult result = producer.send(message);
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭消息生产者
            producer.shutdown();
        }
    }
}

消费消息步骤:
1.创建DefaultMQPushConsumer
2.设置NameServer地址
3.设置subscribe,这里是要读取的主题信息
4.创建消息监听MessageListener
5.获取消息信息
6.返回消息读取状态
7.开启消费者

消费消息测试:

public class TestConsumer {
    public static void main(String[] args) {
        //创建MQ消费者对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
        try {
            //指定NameServer地址
            consumer.setNamesrvAddr("127.0.0.1:9876");
            //订阅topic
            consumer.subscribe("base","tag1");
            //消费消息
            consumer.registerMessageListener(new MessageListenerConcurrently() {
                @Override
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                    for (MessageExt messageExt : list) {
                        System.out.println(new String(messageExt.getBody()));
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });
            //开启消费者MQ
            consumer.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

消费消息模式分为两种:广播模式、负载均衡模式(默认)
setMessageModel方法指定选用哪种消费模式

顺序消息:
发送消息的顺序和消费消息的顺序一致
消息队列选择器

发送顺序消息测试:

public class OrderProducer {
    public static void main(String[] args) throws Exception {
        //创建消息生产者MQ
        DefaultMQProducer producer = new DefaultMQProducer("group1");
        //指定NameServer地址
        producer.setNamesrvAddr("127.0.0.1:9876");
        //启动生产者
        producer.start();
        List<Order> order = Order.getOrder();
        //发送顺序消息
        for (int i = 0; i < 9; i++) {
            String details = order.get(i).getOrderDetails();
            //创建消息对象
            if(!StringUtils.isEmpty(details)){
                Message message = new Message("base","tag1",details.getBytes());
                producer.send(message, new MessageQueueSelector() {
                    @Override
                    public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
                        Integer index = (Integer)o % list.size();
                        return list.get(index);
                    }
                },order.get(i).getOrderId());
            }
        }
        //关闭生产者
        producer.shutdown();
    }
}

消费顺序消息测试:

public class OrderConsumer {
    public static void main(String[] args) throws Exception {
        //创建消费者MQ对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
        //指定NameServer地址
        consumer.setNamesrvAddr("127.0.0.1:9876");
        //订阅topic
        consumer.subscribe("base","tag1");
        //消费消息
        consumer.registerMessageListener(new MessageListenerOrderly() {
            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
                for (MessageExt messageExt : list) {
                    System.out.println("线程"+Thread.currentThread().getName()+"消费了消息:"+new String(messageExt.getBody()));
                }
                return ConsumeOrderlyStatus.SUCCESS;
            }
        });
        //开启消费消息MQ
        consumer.start();
    }
}

延迟消息:

延迟消息级别,不能直接写时间,只能设定指定的时间间隔
在这里插入图片描述
延迟消息发送测试

public class DelayLeveProducer {
    public static void main(String[] args) throws Exception {
        //创建消息生产者MQ
        DefaultMQProducer producer = new DefaultMQProducer("group1");
        //指定NameServer地址
        producer.setNamesrvAddr("127.0.0.1:9876");
        //启动生产者
        producer.start();
        List<Order> order = Order.getOrder();
        //发送顺序消息
        for (int i = 0; i < 9; i++) {
            String details = order.get(i).getOrderDetails();
            //创建消息对象
            if(!StringUtils.isEmpty(details)){
                Message message = new Message("base","tag1",details.getBytes());
                //设置延迟消息时间
                message.setDelayTimeLevel(3);
                //发送消息
                producer.send(message, new MessageQueueSelector() {
                    @Override
                    public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
                        Integer index = (Integer)o % list.size();
                        return list.get(index);
                    }
                },order.get(i).getOrderId());
            }
        }
        //关闭生产者
        producer.shutdown();
    }
}

事务消息:
正常事务消息的发送、提交,事务消息的补偿流程

事务消息发送测试:

public static void main(String[] args) throws Exception {
    //创建事务消息生产者
    TransactionMQProducer producer = new TransactionMQProducer("group1");
    //指定NameServer地址
    producer.setNamesrvAddr("127.0.0.1:9876");
    //启动事务生产者
    producer.start();
    //事务监听
    producer.setTransactionCheckListener(new TransactionCheckListener() {
        @Override
        public LocalTransactionState checkLocalTransactionState(MessageExt messageExt) {
            String tags = messageExt.getTags();
            if(StringUtils.pathEquals(tags,"C")){
                return LocalTransactionState.COMMIT_MESSAGE;
            }
            return null;
        }
    });
    //发送消息
    String tags[] = {"A","B","C"};
    for (int i = 0; i < 3; i++) {
        Message msg = new Message("TransationTopic",tags[i],("Hello 事务消息"+i).getBytes());
        producer.sendMessageInTransaction(msg, new LocalTransactionExecuter() {
            @Override
            public LocalTransactionState executeLocalTransactionBranch(Message message, Object o) {
                if(StringUtils.pathEquals("A",message.getTags())){
                    return LocalTransactionState.COMMIT_MESSAGE;
                }else if(StringUtils.pathEquals("B",message.getTags())){
                    return LocalTransactionState.ROLLBACK_MESSAGE;
                }else {
                    return LocalTransactionState.UNKNOW;
                }
            }
        },tags[i]);
    }
}

事务消息消费测试:

public static void main(String[] args) throws Exception {
    //创建消费者MQ对象
    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
    //指定NameServer地址
    consumer.setNamesrvAddr("127.0.0.1:9876");
    //订阅topic
    consumer.subscribe("TransationTopic","*");
    //消费消息
    consumer.registerMessageListener(new MessageListenerOrderly() {
        @Override
        public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
            for (MessageExt messageExt : list) {
                System.out.println("线程"+Thread.currentThread().getName()+"消费了消息:"+new String(messageExt.getBody()));
            }
            return ConsumeOrderlyStatus.SUCCESS;
        }
    });
    //开启消费消息MQ
    consumer.start();
}

整合SpringBoot
1.导入pom依赖

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

2.配置文件

rocketmq:
  name-server: 127.0.0.1:9876  #指定nameserver的地址,如果是集群可以用逗号隔开
  producer:
    group: springboot-rocketmq-gmall    #指定消息发送到哪个组

3.发送消息

@RestController
@RequestMapping("/rocketmq")
public class RocketMQProducer {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @RequestMapping("/send")
    public String send(){
        String topic = "springboot-rocketMQ";
        rocketMQTemplate.convertAndSend(topic,"hello springboot-rocketMQ");
        return "发送消息成功";
    }
}

4.消费消息

@RocketMQMessageListener(consumerGroup = "rocketmq-consumer",topic = "springboot-rocketMQ")
@Component
public class RocketMQConsumer implements RocketMQListener<String> {
    @Override
    public void onMessage(String s) {
        System.out.println("消费的消息是"+s);
    }
}

消息存储(持久化):
消息存储顺序写保证消息写入磁盘的速度
RocketMQ利用零拷贝技术提高消息存盘和网络发送速度
在这里插入图片描述
刷盘机制:
在这里插入图片描述
高可用性:
NameServer集群、Broker集群、Producer集群、Consumer集群

Broker主从复制:
保证主从服务的消息一致性
同步复制
当消息生产者发送消息后,master将消息同步到slave,当同步成功之后才会返回发送成功的状态,这样的话就会有延迟,效率低;
异步复制
当消息生产者发送消息后,只要master接收到了消息,那么直接会返回消息发送成功的状态,然后另外开启一个线程去将消息同步到slave,效率会比较高;可能出现写入slave的时候master宕机了,那么消息就会丢失;
生产者一般使用异步刷盘+同步复制的方案,既保证吞吐量又保证消息可靠性

负载均衡:
生产者负载均衡默认开启,利用topic每次都分发到不同的消息队列
消费者负载均衡,多个消费者会均摊消息队列,每个消费者消费固定的消息队列中的消息

消息重试机制:
分为顺序消息重试和无序消息(普通、定时、延时、事务)重试
顺序消息默认是重试的,前面的消息没被消费后面的消息不会被消费,会产生消息阻塞的情况
只有集群模式支持重试机制,广播模式不支持重试机制

重试次数:
16次重试还没消费成功消息,消息将会进入死信队列在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
死信队列:
当消息重试次数超过最大次数后,消息将会放入死信队列;
不会再被消费者正常消费
有效期为3天,3天后自动删除,因此需要3天内去处理这些死信消息
一个死信队列对应一个group,和topic无关

消息幂等性:
网络波动或者发送消息时宕机或者消费者消息时宕机等等,都会出现消息重复的原因,那么必须有一个幂等性校验消费;
每次消费消息去redis查看当前消息id存不存在,存在表示消费过直接丢弃,不存在表示没消费,可以消费此消息,消费完消息后将消息id存入redis中方便下一次做校验;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值