rocketmq 概述


rocketmq 概述

 

********************************

集群组成

 

         

 

name server:在内存中存储集群的元数据,定时接受各节点上传数据,并为proudcer、consumer提供路由服务;name server之间相互独立,无节点通信

broker:存储消息,并与name server建立长连接,定时发送心跳;可主从部署,提高集群的可用性

producer:从name server获取元数据,与broker建立长连接,发送消息

consumer:从name server获取元数据,与broker建立长连接,消费消息

启动流程:先开启name server,再开启broker,然后使用producer、consumer收发消息

 

 

********************************

name server

 

name server中的数据存储在内存,不会持久化到磁盘;

name server可部署多个,相互之间独立,接受broker上传元数据信息;

producer、consumer从name server拉取元数据,路由到broker进行消息收发

 

name server存储的元数据

public class RouteInfoManager {
    private static final InternalLogger log = InternalLoggerFactory.getLogger("RocketmqNamesrv");
    private static final long BROKER_CHANNEL_EXPIRED_TIME = 120000L;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final HashMap<String, List<QueueData>> topicQueueTable = new HashMap(1024);
                          //key为topic name,value为topic对应的队列

    private final HashMap<String, BrokerData> brokerAddrTable = new HashMap(128);
                          //key为brokerName,value为有相同brokerName的broker信息

    private final HashMap<String, Set<String>> clusterAddrTable = new HashMap(32);
                          //key为clueterName,vlaue为同一clusterName下的brokerName集合

    private final HashMap<String, BrokerLiveInfo> brokerLiveTable = new HashMap(256);
                          //key为brokerAddr,vlaue为broker的状态信息

    private final HashMap<String, List<String>> filterServerTable = new HashMap(256);
                          //key为brokerAddr,vlaue为broker上的过滤服务器的名称

*****************************************

public class QueueData implements Comparable<QueueData> {
    private String brokerName;
    private int readQueueNums;
    private int writeQueueNums;
    private int perm;
    private int topicSynFlag;


**********************************************

public class BrokerData implements Comparable<BrokerData> {
    private String cluster;
    private String brokerName;
    private HashMap<Long, String> brokerAddrs;

 

 

********************************

消息存储 broker

 

broker负责接收producer发送的消息进行存储,并接受消费端的消息拉取请求;

同时进行主从部署,将消息发送给slave broker,实现主从备份,部署多主多从,实现高可用;

broker也提供消息的过滤服务


存储结构

 

      

 

   

commitLog:存储消息,producer端发送的消息按顺序写入commitlog,commitLog是一个目录,实际使用mapfile文件保存消息,目录中可包含多个mapfile文件,每个mapfile文件大小为1g,文件剩余空间不足则新建mapfile文件存储消息;每台broker上的commitLog被本机上所有的consume queue共享

consume queue:类似于数据库索引文件,消息写入commitlog中后,会将消息在commitlog中的偏移量等信息发送到consume queue(每个topic下的message queue都对应一个consume queue,consumer根据消息偏移量读取commitlog中的消息;每个consume queue可存储30万个节点,consume queue的内容也会写到磁盘持久存储;

index file:索引文件,根据key查询消息,使用hash+链表存储节点,方便快速查询消息;index file是一个目录,包含多个文件,单个index文件有500万个hash槽,存储2000万个节点

public class ConsumeQueue {      //consume queue是个文件,每个节点存储消息在commitLog中的偏移量
    private static final InternalLogger log = InternalLoggerFactory.getLogger("RocketmqStore");
    public static final int CQ_STORE_UNIT_SIZE = 20;
    private static final InternalLogger LOG_ERROR = InternalLoggerFactory.getLogger("RocketmqStoreError");
    private final DefaultMessageStore defaultMessageStore;
    private final MappedFileQueue mappedFileQueue;
    private final String topic;       //topic 名称
    private final int queueId;        //队列id
    private final ByteBuffer byteBufferIndex;
    private final String storePath;   //consume queue存储路径
    private final int mappedFileSize;
    private long maxPhysicOffset = -1L;
    private volatile long minLogicOffset = 0L;
    private ConsumeQueueExt consumeQueueExt = null;


****************
MessageQueue:对应topic在某个master broker上的consume queue

public class MessageQueue implements Comparable<MessageQueue>, Serializable {
    private static final long serialVersionUID = 6191200464116433425L;
    private String topic;             //topic 名称
    private String brokerName;        //broker 名称
    private int queueId;              //队列id

 

 

文件刷盘方式:同步刷盘、异步刷盘

同步刷盘:文件写入磁盘后返回写入成功状态

异步刷盘:文件提交到内存后,即可返回写成功状态,由后台线程负责刷盘,将文件写入磁盘

 

主从同步方式:同步主从、异步主从

同步主从:消息写入slave broker成功后,返回写成功状态

异步主从:消息发送给slave broker后即可返回,不需要接受slave broker的写入状态

 

 

********************************

消息发送

 

消息发送流程

设置发送组、name server、发送失败重试次数等信息;

创建要发送的消息对象;

启动producer,选择消息发送方式发送消息,根据发送方式不同进行不同的处理

说明:当topic下的某个master故障时,会自动向该topic下的可用master发送消息,保证了发送端的高可用;创建topic时,需将topic分布在多个master上,保证消息发送的高可用;如果只将topic创建在一个master上,则master故障后,消息不会正常发送;

4.5版本后,提供了主从自动切换,master故障后,slave会自动切换为master,将topic创建在一个master上,master故障后消息仍可正常发送

 

 

消息发送方式

同步发送:发送端等待获取broker响应信息,写入成功后写后续消息

异步发送:设置回调函数,发送后不需要等待就可继续写后续消息

单向发送:不等待获取broker端的响应信息,不设置回调函数

 

 

消息发送的返回状态

public enum SendStatus {
    SEND_OK,              //消息正常发送
    FLUSH_DISK_TIMEOUT,   //同步刷盘是没在规定的时间完成刷盘
    FLUSH_SLAVE_TIMEOUT,  //同步主从,slave刷盘超时
    SLAVE_NOT_AVAILABLE;  //同步主从,slave异常

    private SendStatus() {
    }
}

 

消息类型:普通消息、顺序消息、延时消息、事务消息

普通消息:不对消息设置延时发送,不顺序消费或者需要事务消费

顺序消息:消息的发送、消费有先后顺序,一般使用局部顺序消息,producer将需要顺序消费的消息发送到同一个队列;顺序消息消费的时consumer需要对consume queue加锁,broker端锁的存活时间默认为60s,持有锁的consumer故障后锁会自动释放

延时消息:消息到达broker后,按照指定的级别延时消费

事务消息:消息是否消费需要根据本地函数的执行状态来决定是否需要消费

 

消息重发

同步发送:发送异常、超时未收到响应信息

异步发送:超时时间内收到异常响应信息(超时未收到响应信息不会重发)

单向发送:消息不会重发,消息可能会丢失,适用于消息发送要求不高的场景

 

消息重复:同步发送时消息写入成功,但是超时未收到响应信息导致消息重发,造成消息重复

解决方法:消费端幂等(多次调用与一次调用效果相同);

维护已消费消息记录,消费消息前查询消息是否已经消费

 

 

********************************

消息消费

 

消息消费流程

设置消费组、name server、消费模式等信息;

订阅topic,设置过滤标识tag;

注册消息监听(并发或者顺序消费),从consume queue拉取消息消费;

启动consumer处理消息

说明:消息消费不需要设置从master、slave读取,如果master繁忙或者故障时,consumer会自动切换到slave读取消息,保证了消费端的高可用

 

消费模式:广播消费、集群消费

广播消费:消息被consumer group中所有的consumer消费,消费偏移量offset存储在消费端

集群消费:消息只会被consumer group中的一个consumer消费,消费偏移量offset存储在broker端

 

消息拉取过程(长轮询)broker收到consumer的获取消息的请求,如果有消息则返回消息;如果没有消息,每隔5秒查看是否有消息,如果15秒依然没有消息,则返回空结果,在此期间,如果有消息达到,则直接返回消息

 

consumer拉取消息:消费流量控制

DefaultMQPushConsumer消息拉取的主动权在consumer端,consumer为每个message queue分配一个processQueue对象,从consumer queue拉取的消息全部存储在processQueue中;当processQueue存储的未处理消息个数、大小或者偏移量超过设定的值时,则过一段时间再去拉取消息

public class ProcessQueue {
    public static final long REBALANCE_LOCK_MAX_LIVE_TIME = Long.parseLong(System.getProperty("rocketmq.client.rebalance.lockMaxLiveTime", "30000"));
    public static final long REBALANCE_LOCK_INTERVAL = Long.parseLong(System.getProperty("rocketmq.client.rebalance.lockInterval", "20000"));
    private static final long PULL_MAX_IDLE_TIME = Long.parseLong(System.getProperty("rocketmq.client.pull.pullMaxIdleTime", "120000"));
    private final InternalLogger log = ClientLogger.getLog();

    private final ReadWriteLock lockTreeMap = new ReentrantReadWriteLock();
                                                             //读写锁控制线程对消息的并发访问
    private final TreeMap<Long, MessageExt> msgTreeMap = new TreeMap();
                                                             //存储拉取的的消息,key为消息偏移量,value为消息
    private final AtomicLong msgCount = new AtomicLong();    //拉取的消息数量
    private final AtomicLong msgSize = new AtomicLong();     //拉取的消息大小

    private final Lock lockConsume = new ReentrantLock();    //使用锁实现消息的顺序消费
    private final TreeMap<Long, MessageExt> consumingMsgOrderlyTreeMap = new TreeMap();  
                                                             //顺序消费时存储从broker中拉取的消息

    private final AtomicLong tryUnlockTimes = new AtomicLong(0L);
    private volatile long queueOffsetMax = 0L;
    private volatile boolean dropped = false;
    private volatile long lastPullTimestamp = System.currentTimeMillis();
    private volatile long lastConsumeTimestamp = System.currentTimeMillis();
    private volatile boolean locked = false;
    private volatile long lastLockTimestamp = System.currentTimeMillis();
    private volatile boolean consuming = false;
    private volatile long msgAccCnt = 0L;

    public ProcessQueue() {
    }


    ...


    public long getMaxSpan() {      //获取消息跨度
        try {
            this.lockTreeMap.readLock().lockInterruptibly();

            long var1;
            try {
                if (this.msgTreeMap.isEmpty()) {
                    return 0L;
                }

                var1 = (Long)this.msgTreeMap.lastKey() - (Long)this.msgTreeMap.firstKey();
            } finally {
                this.lockTreeMap.readLock().unlock();
            }

            return var1;
        } catch (InterruptedException var7) {
            this.log.error("getMaxSpan exception", var7);
            return 0L;
        }
    }

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值