首页 > 其他分享 >【RocketMQ】NameServer总结

【RocketMQ】NameServer总结

时间:2023-05-25 23:47:25浏览次数:48  
标签:总结 请求 Broker 发送 注册 心跳 NameServer RocketMQ

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() {

            @Override
            public 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服务注册

标签:总结,请求,Broker,发送,注册,心跳,NameServer,RocketMQ
From: https://www.cnblogs.com/shanml/p/17433314.html

相关文章

  • 王道--第四章 文件管理 习题总结
    4.1.8.1UNIX操作系统中,输入/输出设备视为()A.普通文件B.目录文件C.索引文件D.特殊文件UNIX操作系统中,所有设备都被视为特殊的文件,因为UNIX操作系统控制和访问外部设备的方式和访问一个文件的方式是相同的,选D。 4.1.8.3打开文件操作的主要工作是()A.把指定文件的目录项复制......
  • 树状数组学习总结
    今天本初中生蒟蒻学习了一下\(\color{red}{树状数组}\),总结一下~~~树状数组的实现功能简介快速求前缀和(\(\color{purple}{O(log_2n)}\))修改某一个数(\(\color{green}{O(log_2n)}\))树状数组图示树状数组其实就是如图所建立的~~~下面引入一个函数——lowbitlowbit(x)是x......
  • 今日总结
    今天报告作为学生会的一员,今天上午跟着查了宿舍卫生。中午写了互联网+的项目计划书,上体育课,体育课考试没考好因为下周三就要考试了,数据库原理与应用,所以现在在复习代码时间:0h代码行数:0行博客数量:0篇......
  • 2023.5.10周三每日总结
     异步处理Android应用程序中,获取网络数据需要使用异步任务的方式,以避免界面卡顿、假死等。在AS中,我们可以使用异步任务或Handler来避免程序挂起。深入了解异步处理,可以更好的掌握跨线程间的数据处理。......
  • 2023.5.15周一每日总结
    这周老师为我们讲解了人机交互设计像我们说明了合理的设计的重要性通过带我们分析茶壶的组成,和茶壶茶嘴等拼接在一起的方式的不同,像我们说明一个合理的ui的重要性错误示例: 我们说软件工程终究是和人打交道的行业,我们需要满足用户的要求 而要做到这一点,我们需要有很强的......
  • 2023.4.26周三每日总结
    Activity的生命周期Activity生命周期是一个非常重要的概念,理解Activity的生命周期对于开发Android应用程序至关重要。在AS中,Activity有始有终,可以选择对应状态的回调函数,根据状态完成一些逻辑操作。学习Activity的生命周期,可以更好的掌握应用程序的启动、销毁、状态保存等操作......
  • 关于软件构造第二部分(PPT4-8)的总结复习
    一、基本数据类型、对象数据类型基本数据类型:int、long、boolean、double等,——有值,无ID,无法区分,不可变,在栈中分配内存,代价低;对象数据类型:String、Date等——有值,有ID,可为可变也可为不可变,在堆中分配内存,代价昂贵;可将基本数据类型包装为动态数据类型(首字母变大写)通常在定义集合......
  • 2023.5.1周一每日总结
    虽然今天是劳动节,但我依旧进行了Android的学习今天所学习的内容是intentIntentIntent是在不同Activity、应用程序之间传递信息的途径。在AS中,我们可以使用Intent来启动另一个Activity或应用程序,也可以传递数据到其他程序。学习Intent,可以更好地掌握跨应用程序间的通信。在An......
  • 2023.5.6周六每日总结
    网络连接Android应用程序最广泛的使用之一是网络通信。为了在AS中进行网络通信,可以使用Java的URL和HttpURLConnection或者像Volley等框架库。学习网络连接可以实现调用API接口,获取服务器端资源等。api接口调用大多数在虚拟机中不会出现太多的问题,但在手机上实际使用安卓时会......
  • 2023.4.22周六每日总结
    控件使用控件就是Android应用程序中各种元素,如按钮、文本框、显示列表等等。学习使用控件是Android开发的基础,它是许多程序界面的桥梁。在AS中,通过拖拽、代码编写等方式添加或修改控件,并通过属性面板修改控件的属性。在这里的学习中为了使按钮能发挥我想要的作用,进行了多次调......