第二章:如何运行

第二章:如何运行,第1张

第二章:如何运行 知识前提

阅读本篇文章您应该了解或掌握一些前置知识: 基本概念 特性

聊聊NameServer

NameServer是一个Broker与Topic路由的注册中心,支持Broker的动态注册与发现。

在早期版本的时候(metaQ v2.0版本及之前),依赖的仍然是Zookeeper。后来RoketMQ开始去掉了Zookeeper依赖,使用了自己的NamerServer。

主要包括下面功能:

Broker管理:接受Broker集群的注册信息并保存下来作为路由信息的基本数据;提供心跳检测机制,检查Broker是否还存活。路由信息管理:每个NameServer中都保存着Broker集群的整个路由信息和用于客户端查询的队列信息。Producer和Conumser通过NameServer可以获取整个Broker集群的路由信息,从而进行消息的投递和消费。

路由注册

​ NameServer通常以集群的方式部署,但NameServer是无状态的,它在集群中的各个节点之间是无差异的,各个节点相互相互之间没有信息通讯。那各个节点中的数据是如何进行数据同步的呢?

​ 在Broker节点启动时,遍历NameServer列表,与每个NameServer节点建立长连接,发起注册请求。

在NameServer内部维护着一个Broker列表,用来动态的存储Broker信息

​ 这种注册中心的机制与其他产品有所不同,它这种无状态的方式优点在于搭建NameServer集群、扩容简单,但是要注意对于Broker,必须要指出所有NameServer的地址。否则将不会去注册,也正因为这样,NameServer并不能随便进行扩容。若新增了NameServer服务器而没有重新配置Broker,新增的NameServer对于Broker来说是无效的,是不可见。

​ Broker节点为了证明自己是活跃的,还要维护与NameServer之间的长连接,它会将最新的信息以心跳包的方式上报给NameServer,每30秒发送一次心跳。心跳包中包含BrokerId、Broker地址(IP+Port)、Broker名称、Bro所属集群名称等。NamerServer在接收到心跳包之后,会更新心跳时间戳,记录这个Broker的最新存活时间。

路由剔除

​ 由于Broker关机、宕机或网络抖动等原因,NameServer没有收到Broker的心跳,NameServer可能会将其从Broker列表中剔除。

​ NameServer中有个定时任务,每隔10秒就会扫描一次Broker表,查看每一个Broker的最新心跳时间戳距离当前时间是否超过120秒,如果超过那么就判定这个Broker失效了,并将其从表中剔除。

路由发现

​ RocketMQ的路由发现采用的是Pull模型(拉取模型)。当Topic路由信息出现变化时,NameServer不会主动推送给客户端,而是客户端定时拉取主题最新的路由。默认客户端每30秒会拉取一次最新的路由。

1)Push模型:推送模型,这种模型的实时性较强,是一个“发布-订阅”模型,需要维护一个超链接。很显然长连接的维护是需要资源成本的。

2)Pull模型:定时拉取消息,实时性较差

3)Long Polling模型:长轮询模型,是对Push和Pull两种模型的整合,充分利用了二者中的优势,屏蔽了它们的劣势。

客户端NameServer选择策略

​ 这里的客户端指的是Producer与Consumer。

​ 客户端在配置时必须要写上NameServer集群的地址,那么客户端到底连接的是哪个NameServer节点呢?

客户端首先会产生一个随机数,然后再与NameServer节点数量取模,此时得到的就是要连接的节点索引,然后就会进行连接。如果连接失败,则会采用round-robin策略(随机的轮询),逐个尝试着去连接其它节点。

这里的选择跟Zookeeper Client的选择有点相似:

​ Zookeeper经过两次Shuffle,然后选择第一台Zookeeper Server(打散后随即挑一个)

Broker原来如此

功能介绍

​ Broker充当着消息中转角色,负责存储消息、转发消息。Broker在RocketMQ系统中负责接收并存储从生产者发送来的消息,同时为消费者的拉取请求作准备。Broker同时也存储着消息相关的元数据,包括消费者组消费进度偏移offset、主题、队列等。

​ 为了增强Broker性能与吞吐量,Broker一般都是以集群的形式出现。各集群节点中可能存放着相同Topic的不同Queue。不过,这里有个问题,如果某Broker节点宕机,如何保证数据不丢失呢?

​ 将每个Broker集群节点进行横向扩展,既将Broker节点再建为一个HA集群,解决单点问题。

​ Broker节点集群是一个主从集群,既集群中具有Master与Slave两种角色。Master负责处理读写请求,Slave负责对Master中的数据进行备份。当Master挂掉时,Slave则会自动顶替Old Master的工作。一个Master可以有多个Slave,一个Slave只能隶属于某一个Master。这个Broker集群是主备集群Master与Slave的对应关系是通过指定相同的BrokerName、不同的BrokerId来确定的。BrokerId为0时表示Master,非0则为Slave。每个Broker与NameServer集群中的所有节点建立长连接,定时注册topic消息到所有的NameServer。

主备:自己既能读也能写,slave只担任备份角色

工作流程

1)启动NameServer,NameServer启动后开始监听端口,等待Broker、Producer、Consumer连接。

2)启动Broker,启动时Broker会与所有的NameServer建立并保持长连接,然后每30秒向NameServer定时发送心跳包。

3)发送消息前,可以先创建Topic,创建Topic时需要指定该Topic要存储在哪个Broker上,当然,在创建Topic时也会将Topic与Broker的关系写入到NameServer中。不过,这一步是可选的,也可以在发送消息时自动创建Topic。

4)Producer发送消息,启动时先跟NameServer集群中的某一台建立长连接,并从NameServer中获取路由信息,既当前发送的Topic消息的Queue与Broker的地址(IP+Port)的映射关系。然后根据算法策略选择一个Queue,与队列所在的Broker建立长连接从而向Broker发消息。当然,在获取到路由信息后,Producer会首先将路由信息缓存到本地,再每30秒从NameServer Pull一次路由信息。

5)Consumer跟Producer类似,不同点在于,Consumer会向Broker发送心跳,以确保Broker的存活状态。

读/写队列

​ 从物理上来讲,读/写队列是同一个队列。所以,不存在读/写队列数据同步问题。它是逻辑上进行区分的概念。

例如:创建Topic时设置的写队列数量为8,读队列数量为4,此时系统会创建8个Queue[0,1,2,3,4,5,6,7],Producer会将消息写入到这8个队列,但Consumer只会消费[0,1,2,3]这4个队列中的消息,后面的消息是无法消费的。

​ 上述存在一个读与写队列数不一样的问题,其实这样设计是有好处的:比如现有16个Queue,但是目前不需要这么多了,我们可以先将写队列修改为8个,这样每次读只能读前面8个,等后8个Queue中的消息消费完毕后,就可以直接将读队列的数量动态设置为8,这样整个调整过程不会丢失如何消息。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5719728.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存