读书笔记-RocketMQ实战与原理解析

rocketmq的四个角色

producer,comsumer,broker,nameserver

rocketmq各个角色之间的关系


rocketmq使用前需要先新建topic,然后根据topic发送和接收消息

rocketmq集群方法



rocketmq使用demo

https://gitee.com/liran123/rocketmq_demo

同步,异步发送消息
public SendResult syncSend(Integer id) {

        // 创建 Demo01Message 消息
        Demo01Message message = new Demo01Message();
        message.setId(id);
        // 同步发送消息
        return rocketMQTemplate.syncSend(Demo01Message.TOPIC, message);
    }

    public void asyncSend(Integer id, SendCallback callback) {
        // 创建 Demo01Message 消息
        Demo01Message message = new Demo01Message();
        message.setId(id);
        // 异步发送消息
        rocketMQTemplate.asyncSend(Demo01Message.TOPIC, message, callback);
    }

    public void onewaySend(Integer id) {
        // 创建 Demo01Message 消息
        Demo01Message message = new Demo01Message();
        message.setId(id);
        // oneway 发送消息
        rocketMQTemplate.sendOneWay(Demo01Message.TOPIC, message);
    
}
接收消息
@Component

@RocketMQMessageListener(
        topic = Demo03Message.TOPIC,
        consumerGroup = "demo03-consumer-group-" + Demo03Message.TOPIC
)
public class Demo03Consumer implements RocketMQListener<Demo03Message> {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void onMessage(Demo03Message message) {
        logger.info("[onMessage][线程编号:{} 消息内容:{}]", Thread.currentThread().getId(), message);
    }


}

不同类型的消费者








DefaultMQPushConsumer处理流程

consumer启动和关闭流程

当consumer为pull模式,启动可以自己控制,关闭时需要保存offset,需要在异常处理阶段增加把offset写入磁盘的处理,记住了每次关闭的offset,才能保证消息准确性
push模式,

关闭时需要调用shutdown函数,释放资源,保存offset

生产者

默认使用DefaultMqProducer类

简单生产者
public static void main(String[] args) throws MQClientException, InterruptedException {

        DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
        producer.setNamesrvAddr("192.168.20.4:9876");
        producer.start();
        for (int i = 0; i < 10000; i++) {
            try {

                Message msg = new Message("TopicTest" /* Topic */,
                    "TagA" /* Tag */,
                    ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
                );
                SendResult sendResult = producer.send(msg);
                System.out.printf("%s%n", sendResult);
            } catch (Exception e) {
                e.printStackTrace();
                Thread.sleep(1000);
            }
        }
        producer.shutdown();
    }
简单消费者
 public static void main(String[] args) throws InterruptedException, MQClientException {

        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4");
        consumer.setNamesrvAddr("192.168.20.4:9876");

        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        consumer.subscribe("TopicTest77777", "*");

        consumer.registerMessageListener(new MessageListenerConcurrently() {

            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
                ConsumeConcurrentlyContext context) {
                System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });

        consumer.start();

        System.out.printf("Consumer Started.%n");
    }

nameserver

  • nameserver是整个消息队列的状态服务器,各个角色要定期向nameserver上报自己的状态信息
  • nameserver与各个组件交流的流程

topic

通过命令行操作进行创建

创建topic的命令会被发送到对应的broker,然后执行具体的逻辑

消息队列核心机制

服务器要将文件内容从磁盘发送到客户端,需要经历两个步骤

tmp_buf是预先申请的内存
从磁盘复制数据到内核态内存,从内核态内存复制到用户内存 完成read操作
从用户态内存复制到网络驱动的内核态内存,最后从网络驱动的内核台内存复制到网卡中进行传输 完成write操作

通过mmap方式,可以省去向用户态内存复制,提高速度
在java7中的mappedbytebuffer实现


顺序消息

分为全局顺序消息和部分顺序消息
rocket默认情况下会新建8个读队列,8个写队列,消息顺序不保证一致

全局有序

局部有序


发送端通过MessageQueueSelector类来控制把消息发往哪个MessageQueue



消费者端通过MessageListenerOrderly解决/messageQueue消息被并发处理的问题

在MessageListenerOrderly实现中,通过为每个Queue加锁,消费每个消息前都要先获取锁, 保证同一时间,同一个Queue不会被重复消费,但不同的queue可以并发处理

消息重复问题

![](https://img2020.cnblogs.com/blog/924254/202102/924254-20210219165648328-396085952.png

消息优先级的问题




broker端进行消息过滤:减少流到consumer的消息

通过tag,key,sql表达式等方式过滤
一个应用最好使用一个topic,然后不同类型的消息子类型用Tag来标识,服务端基于tag进行过滤,并不需要读取消息体的内容,效率比较高
key一般用消息在业务层面的唯一标识码来表示,尽量使key唯一

posted @ 2021-02-17 19:48  余***龙  阅读(309)  评论(0编辑  收藏  举报