问题描述
HDFS 集群中,有时会因为 NameNode 的超负荷运行,导致部分用户的请求响应缓慢,甚至得不到响应。
原因分析
这种阻塞情况是由于 Hadoop 的初始设计造成的。
在 HDFS 中,NameNode 作为单独的机器,在其 NameSpace 内部协调 HDFS 的各种操作,这些操作包括获取数据块位置、列出目录、创建文件等等。NameNode 接收各个 HDFS Client 的请求,将这些请求视作 RPC 调用置入 FIFO 队列,供处理线程处理。虽然 FIFO 在先到先服务的情况下足够公平,但如果某个用户执行的 I/O 操作较多,那么相比其他 I/O 操作较少的用户,他将获得更多服务,在这种情况下,FIFO 有失公平性并且会导致其它用户的延迟大大增加。
基于 FIFO 调用队列的 NameNode 请求处理过程如下:
解决方案
如果将 FIFO 队列替换为一种被称为公平队列(FairCallQueue)的新型队列,这种情况将会得到改善。
按照这种新型方法,Fair 队列会根据调用者的调用规模,将其传入的 RPC 请求分配至多个队列中,调度模块会跟踪最新的调用,并为调用量较小的用户分配更高的优先级。
基于 Fair 队列的 NameNode 请求处理过程如下:
Fair 调用队列通过在内部调整 RPC 调用的顺序确保服务质量。
该队列由三个部分组成:
- 调度模块(默认为 DecayRpcScheduler),用于提供从 0 至 N 的优先级数字(0 的优先级最高)。
- 多级队列(位于 FairCallQueue 内部),保持 RPC 请求在内部按优先级排列。
- 多路选择器(默认为 WightedRoundRobinMultiplexer),取出一个 RPC 请求时,为目标队列的选择提供逻辑控制。
在对 Fair 调用队列进行配置后:
- 由调度模块决定将新收到的 RPC 请求分配至哪个子队列,当前调度模块为 DecayRpcScheduler,该调度模块会持续对各个用户的调用优先级进行追踪,并周期性调整统计数据。
- 由多路选择器决定下一个要处理的 RPC 请求从哪个子队列出队。
相关配置
下表中的 port 代表 RPC 服务端口,对于 NameNode 来说,即为9000
配置项 | 描述 | 默认值 |
---|---|---|
ipc.< port >.callqueue.impl | 队列的实现类,需要配置为 org.apache.hadoop.ipc.FairCallQueue | LinkedBlockingQueue |
ipc.< port >.faircallqueue.priority-levels | FairCallQueue 中队列的个数,其值必须大于等于1 | 4 |
ipc.< port >.identity-provider.impl | identity provider 的实现类,用于区别 NameNode 上的多个 RPC 调用,当配置为 UserIdentityProvider 时,identity provider 会基于用户为每个调用分配一个标识 | org.apache.hadoop.ipc.UserIdentityProvider |
ipc.< port >.faircallqueue.multiplexer.weights | 各个队列的权值,决定了多路选择器对各个队列的轮询次数 | 该值基于队列数量,在默认情况下,从队列0到队列3,其权值依次为为 8、4、2、1,即队列0轮询8次,然后接着队列1轮询4次,接着队列2轮询2次,最后队列3轮询1次,周而复始 |
ipc.< port >.faircallqueue.decay-scheduler.thresholds | 描述各个优先级队列的进入条件,取决于当前用户的调用次数占总的 RPC 调用次数的比例 | 取决于队列数量,默认情况下队列数为4,从队列3到队列1,进入的阈值为 0.5、0.25、0.15,即:若当前用户调用次数占总调用次数的比例超过0.5,则此次调用进入队列3,若超过0.25,则进入队列2,若超过0.15,则进入队列1,否则,进入队列0(0是最高优先级队列) |
ipc.< port >.faircallqueue.decay-scheduler.period-ms | 调度器会周期性的对每个用户的调用统计次数进行处理,逐步淘汰老的统计数据,这个配置决定了扫描周期 | 5000 |
ipc.< port >.faircallqueue.decay-scheduler.decay-factor | 每次扫描时,每个用户的统计调用次数都会衰减一次,这样如果一个用户很久没有产生新的调用,那么若干次以后,他的统计数据就会被清掉,这个配置决定了衰减比例 | 0.5 |
具体配置
公平队列在在较新的 Hadoop 版本中已经 ready,只是默认没有启用,如果要启用,其它配置项都可以保持默认,只需要配置上面表格中的第一项就可以了,即在 core-site.xml 中配置以下属性:
<property>
<name>ipc.9000.callqueue.impl</name>
<value>org.apache.hadoop.ipc.FairCallQueue</value>
</property>
举例说明
假设全部使用默认配置,则:
-
RPC 请求入队流程
调度模块按照当前用户的调用次数占总的 RPC 调用次数的比例,决定将 RPC 请求入到哪个队列:- 若比例超过0.5, 则此次调用进入队列3。
- 若超过0.25,则进入队列2。
- 若超过0.15,则进入队列1
- 否则,进入队列0.
- 若目标队列已满(每个队列最大长度都为1000),则顺延至下一优先级队列,若连队列3都满了,那就等待,直到队列3有可用空间为止。
-
RPC 请求出队流程
多路选择器按照各个优先级队列的权重,进行轮询,即:- 队列0轮询8次,然后接着队列1轮询4次,接着队列2轮询2次,最后队列3轮询1次,周而复始。
- 若目标队列此时为空,则顺延至下一优先级队列取出一个 RPC 请求。
-
效果
按照默认配置,在极端情况下,一个频繁访问的用户的响应速度可以降低到 1/15,NameNode 将腾出更多的处理能力给其它用户,所谓极端情况是指此时用户数足够,其他用户也有适量的 RPC 请求的情况。
网友评论