有哪些网站适合大学生做兼职,wordpress站点安装,wordpress文章排版工具,建设网站提供资料的函NameServer是一个注册中心#xff0c;提供服务注册和服务发现的功能。NameServer可以集群部署#xff0c;集群中每个节点都是对等的关系#xff08;没有像ZooKeeper那样在集群中选举出一个Master节点#xff09;#xff0c;节点之间互不通信。 服务注册 Broker启动的时候会…NameServer是一个注册中心提供服务注册和服务发现的功能。NameServer可以集群部署集群中每个节点都是对等的关系没有像ZooKeeper那样在集群中选举出一个Master节点节点之间互不通信。 服务注册 Broker启动的时候会向所有的NameServer节点进行注册注意这里是向集群中所有的NameServer节点注册而不是只向其中的某些节点注册因为NameServer每个节点都是对等的所以Broker需要向每一个节点进行注册这样每一个节点都会有一份Broker的注册信息。
服务发现 Broker向NameServer注册以后生产者Producer和消费者Consumer就可以从NameServer中获取所有注册的Broker信息并从中选取Broker进行消息的发送和消费。 以生产者为例在NameServer集群部署模式下生产者会从多个NameServer中随机选取一个进行通信从中拉取所有Broker的注册信息并将拉取到的信息进行缓存生产者知道了Broker的信息后就可以得知Topic的分布情况然后选取一个消息队列与其所在的Broker通信进行消息的发送。如果通信的Nameservre宕机消费者会轮询选择下一个NameServer。
为什么需要NameServer?
在使用RocketMQ的时候为了提升性能以及应对高并发的情况一般都会使用多个Broker进行集群部署假设没有注册中心对于Broker来说如果想获取到集群中所有的Broker信息生产者和消费者需要通过某个Broker获取整个集群的信息从而得到Topic的分布情况每个Broker都需要与其他Broker通信来交换信息以此来得到集群内所有Broker的信息在Broker数量比较大的情况下会造成非常大的通信压力。
为什么不使用zookeeper这样的分布式协调组件 首先zookeeper的实现复杂引入zookeeper会增加系统的复杂度并且zookeeper在CAP中选择了CP也就是一致性和分区容错性从而牺牲了可用性为了保持数据的一致性会在一段时间内会不可用。
而NameServer在实现上简单RocketMQ的设计者也许认为对于一个消息队列的注册中心来说一致性与可用性相比可用性更重要一些至于一致性可以通过其他方式来解决。
假如选择了CP的ZooKeeper先不考虑其他原因在ZooKeeper不可用的时候如果有消费者或生产者刚好需要从NameServer拉取信息由于服务不可用导致生产者和消费者无法进行消息的生产和发送在高并发或者数据量比较大的情况下大量的消息无法发送/无法消费影响是极大的而如果选择AP即便数据暂时处于不一致的状态在心跳机制的作用下也可以保证数据的最终一致性所以RocketMQ选择了自己实现注册中心简单并且轻量。
举个例子假如集群中有三个Broker分别为 A、B、C向三台NameServer进行了注册也分别为A、B、C消费者从NameServer中获取到了三个Broker的信息如果此时BrokerA需要停止服务分别通知三台NameServer需要下线从NameServer中剔除该Broker的信息由于网络或者其他原因NameServer A和B收到了下线的请求NameServer C并未收到此时就处于数据不一致的状态如果某个消费者是与NameServer C进行通信会认为Broker还处于可用的状态
对于这种情况首先NameServer与Broker之间会有一个心跳机制NameServer定时检测在某个时间范围内是否收到了Broker发送的心跳请求如果未收到会认为该Broker不可用将其剔除在下面会讲到所以对于NameServer来说尽管数据会暂时处于不一致的状态但是可以保证过一段时间之后恢复数据的一致性也就是最终一致性。
对于消费者来说既然可以从NameServer C中获取到Broker A的信息那么消费者就认为Broker A可用如果发送的消息所在的消息队列在Broker A中就会与Broker A通信进行发送但实际上Broker A实际上是不可用的消息会发送失败所以RocketMQ设计了消息重试机制以及故障延迟机制。
Broker注册
Broker启动后会开启定时向NameServer进行注册发送心跳包的任务发送心跳包的时间间隔可以在配置文件中进行设置但是最长不能超过10s也就是说Broker最长10秒钟会向Nameserver发送一次心跳包。
NameServer收到Broker的注册请求心跳包后会判断Broker之前是否已经注册过如果未注册过将其加入到注册的Broker集合brokerAddrTable中同时也会记录收到注册请求的时间将其加入到brokerLiveTable中里面记录了NameServer收到每个Broker发送心跳包的时间在进行心跳检测的时候根据这个时间戳来判断是否在规定时间内未收到该Broker发送的心跳包。
读写锁 由于NameServer可能同时收到多个Broker的注册以及生产者或者消费者的拉取请求为了保证数据的一致性因为有读写请求同时发生或者写与写请求同时发生在处理相关请求的时候需要加锁为了提高性能使用了ReadWriteLock读写锁处理注册请求时会先添加写锁处理拉取请求时添加读锁这样如果某一时刻都是读的请求可以同时进行互不影响如果有写请求其他请求就需要等锁释放才可以进行往下进行。如果不使用读写锁直接对所有的请求加锁会影响性能实际上读与读之间并不需要加锁。
心跳检测
Nameserver在启动的时候会开启一个用于心跳检测的定时任务每10s执行一次定时扫描处于不活跃状态的Broker如果在规定时间内未收到某个Broker的心跳包会认为此Broker不可用需要将其进行剔除。
上面说到brokerLiveTable保存了当前NameServer收到的心跳数据里面记录了每一个Broker最近进行注册/发送心跳的时间戳所以只需遍历brokerLiveTable获取每一个Broker最近一次发送心跳的时间进行判断如果上一次发送心跳的时间 过期时间120s 小于 当前时间也就是超过120s没有收到某个Broker的心跳包则认为此Broker已下线将Broker移除。
Broker下线
正常下线 当Broker下线的时候会向NameServer发起取消注册的请求NameServer收到请求后会将Broker剔除。
异常下线
如果Broker异常宕机或者发送给NameServer的取消注册请求由于某些原因并未发送成功NameServer可能并未感知到Broker的下线由于心跳机制定时检测的功能会在一段时间后发现未收到Broker的心跳请求主动将Broker剔除。
生产者和消费者
生产者和消费者都会定时从NameServer中更新Broker的注册信息默认是30s进行一次更新:
public class MQClientInstance {private void startScheduledTask() {this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {Overridepublic void run() {try {// 更新路由信息 MQClientInstance.this.updateTopicRouteInfoFromNameServer();} catch (Exception e) {log.error(ScheduledTask updateTopicRouteInfoFromNameServer exception, e);}}}, 10, this.clientConfig.getPollNameServerInterval(), TimeUnit.MILLISECONDS);}
}对应的相关源码可参考
【RocketMQ】【源码】NameServer的启动 【RocketMQ】【源码】Broker服务注册