highlight: arduino-light
NameServer
路由管理
Broker消息服务器在启动的时向所有NameServer注册。
消息生产者Producer在发送消息之前先从NameServer获取Broker服务器地址列表然后根据负载均衡算法从列表中选择一台服务器进行发送。
NameServer与每台Broker保持长连接,并且每间隔30S通过心跳检测Broker是否存活,如果120秒内没收到broker的上报消息(NameServer每隔10s扫描一次Broker,移除不活跃的Broker。),那么就认为检测到Broker宕机,则从路由注册表中删除。
但是路由变化不会马上通知消息生产者。这样设计的目的是为了降低NameServer实现的复杂度,在消息发送端提供容错机制保证消息发送的可用性。
NameServer本身的高可用是通过部署多台NameServer来实现,但彼此之间不通讯,也就是NameServer服务器之间在某一个时刻的数据并不完全相同,但这对消息发送并不会造成任何影响,这也是NameServer设计的一个亮点,总之,RocketMQ设计追求简单高效,这也是高可用放弃强一致性的体现。
NameServer的主要作用是为消息的生产者和消息消费者提供关于主题Topic的路由信息,那么NameServer需要存储路由的基础信息,还要管理Broker节点,包括路由注册、路由删除等。
路由元信息
路由元信息类:RouteInfoManager
//集群和brokerName关系 private final HashMap<String/* clusterName */, Set<String/* brokerName */>> clusterAddrTable; //brokerName和broker实例的关系:比如地址&主从等信息 private final HashMap<String/* brokerName */, BrokerData> brokerAddrTable; //topic和brokerName以及 queue对应关系 //topic是一个逻辑概念一个topic下的queue可能在多个broker上面 //比如topicA 可能有2个queue分别在broker-a broker-b private final HashMap<String/* topic */, List<QueueData>> topicQueueTable; //broker实例存活信息,主要是根据上一次和nameServer的心跳通信时间判断 private final HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTable; private final HashMap<String/* brokerAddr */, List<String>/* Filter Server */> filterServerTable;
topicQueueTable: Topic消息队列路由信息,消息发送时根据路由表进行负载均衡
brokerAddrTable: Broker基础信息,包括brokerName、所属集群名称、主备Broker地址
clusterAddrTable: Broker集群信息,存储集群中所有Broker名称
brokerLiveTable: Broker状态信息,NameServer每次收到心跳包是会替换该信息
filterServerTable: Broker上的FilterServer列表,用于类模式消息过滤。
RocketMQ基于定于发布机制,一个Topic拥有多个消息队列,一个Broker为每一个主题创建8个读队列和8个写队列。
多个Broker组成一个集群,集群由相同的多台Broker组成Master-Slave架构,brokerId为0代表Master,大于0为Slave。BrokerLiveInfo中的lastUpdateTimestamp存储上次收到Broker心跳包的时间。
Broker-Topic-Queue之间关系
/*** Topic可以理解为在rocketMq体系当中作为一个逻辑消息组织形式,一般情况下一类业务消息会申请一个topic来实现业务之间隔离。 Topic是一个逻辑上的概念,实际上在每个broker上以queue的形式保存,也就是说每个topic在broker上会划分成几个逻辑队列,每个逻辑队列保存一部分消息数据,但是保存的消息数据实际上不是真正的消息数据,而是指向commit log的消息索引。 每个broker上面的queue保存了该topic的一部分消息。一个Broker可以存放多个Topic的queue。 即一个topic可以对应多个broker 一个broker对应多个queue。 每个queue也维护了对应的brokerName。 一个Topic被创建时,默认几个读写队列? 使用命令是8个 ***/
架构设计
Broker消息服务器在启动的时向所有NameServer注册,消息生产者(Producer)在发送消息时之前先从NameServer获取Broker服务器地址列表,然后根据负载均衡算法从列表中选择一台服务器进行发送。发送到该topic所在的broker的某一个queue上。
每台Broker与NameServer保持长连接,并且Broker每间隔30S会发送一次心跳检测,如果120秒内没收到broker的上报消息(NameServer每隔10s扫描一次Broker,移除不活跃的Broker。),那么就认为检测到Broker宕机,则从路由注册表中删除。
但是路由变化不会马上通知消息生产者。这样设计的目的是为了降低NameServer实现的复杂度,在消息发送端提供容错机制保证消息发送的可用性。当消息发送失败时会继续轮询选择下一个broker来发送消息。
NameServer本身的高可用是通过部署多台NameServer来实现,但彼此之间不通讯,也就是NameServer服务器之间在某一个时刻的数据并不完全相同,但这对消息发送并不会造成任何影响,这也是NameServer设计的一个亮点。
感觉这里和eureka很像。保证高可用的AP。总之,RocketMQ设计追求简单高效
问题1:broker故障感知
如果某一台消息服务器broker宕机了,那么消息生产者如何在不重启服务情况下感知呢?
所有的broker都要和nameServer做通信。通过心跳感知broker是否存活。
同时 nameServer每隔10s扫描一次Broker,移除不活跃的Broker。
移除broker是根据broker的lastUpdateStamp+2分钟是否小于当前时间,如果小于就移除。
即如果broker在2分钟内都没有发送心跳 那么就移除该broker
问题2:消息发送到哪台broker
为了避免消息服务器的单点故障导致的整个系统瘫痪,通常会部署多台消息服务器共同承担消息的存储。
那消息生产者如何知道消息要发送到哪台消息服务器呢?
生产者发送消息到指定的topic时,会根据topic从nameServer拉取所有的broker和broker关联的queue的信息。
遍历轮询每个queue,然后选择一个queque发送信息。
一个topic可以存储在多个broker,一个broker可以存储多个queue。
生产者遍历所有的queue依次发送。
Broker消息服务器在启动的时向所有NameServer注册,消息生产者(Producer)在发送消息时之前先从NameServer获取所有的Broker服务器地址列表,然后根据负载均衡算法从列表中选择一台服务器进行发送。
NameServer与每台Broker保持长连接,并且每间隔30S通过心跳检测Broker是否存活,如果120秒内没收到broker的上报消息,那么就认为检测到Broker宕机,则从路由注册表中删除。但是路由变化不会马上通知消息生产者。NameServer每隔10s扫描一次Broker,移除不活跃的Broker。
这样设计的目的是为了降低NameServer实现的复杂度,在消息发送端提供容错机制保证消息发送的可用性。具体就是故障规避机制。
NameServer本身的高可用是通过部署多台NameServer来实现,但彼此之间不通讯,也就是NameServer服务器之间在某一个时刻的数据并不完全相同,但这对消息发送并不会造成任何影响,这也是NameServer设计的一个亮点,总之,RocketMQ设计追求简单高效。