公平呼叫队列指南

目的

本文档介绍如何配置和管理Hadoop的公平呼叫队列。

先决条件

确保Hadoop已正确安装、配置和设置。更多信息请参阅:

概述

Hadoop服务器组件,特别是HDFS NameNode,会承受来自客户端的极高RPC负载。默认情况下,所有客户端请求都通过一个先进先出队列进行路由,并按到达顺序提供服务。这意味着单个用户提交大量请求很容易使服务不堪重负,导致其他所有用户的服务质量下降。公平调用队列及其相关组件旨在减轻这种影响。

设计细节

IPC堆栈中有几个组件具有复杂的相互作用,每个组件都有自己的调优参数。下图展示了它们相互作用的示意图,将在下文进行解释。

FairCallQueue Overview

在以下说明中,加粗的词语表示命名实体或可配置项。

当客户端向IPC服务器发起请求时,该请求首先会进入监听队列读取器线程从该队列中取出请求,并将其传递给可配置的RpcScheduler以分配优先级并放入调用队列;FairCallQueue就是作为可插拔实现(另一种现有实现是FIFO队列)驻留在此处。处理器线程从调用队列中接收请求,处理它们,并向客户端返回响应。

FairCallQueue默认使用的RpcScheduler实现是DecayRpcScheduler,它会维护每个用户接收到的请求计数。这个计数会随时间衰减;每个扫描周期(默认为5秒),每个用户的请求数会乘以一个衰减因子(默认为0.5)。这样可以维护每个用户请求数的加权/滚动平均值。每次执行扫描时,所有已知用户的调用计数会从高到低排序。根据用户发起的调用占总数的比例,每个用户会被分配一个优先级(默认为0-3,0表示最高优先级)。默认的优先级阈值为(0.125,0.25,0.5),这意味着调用占总次数50%以上的用户(最多只有一个这样的用户)会被分配到最低优先级,调用占25%到50%的用户处于次低优先级,调用占12.5%到25%的用户处于次高优先级,其余用户则被分配到最高优先级。扫描结束时,每个已知用户都会有一个缓存的优先级,该优先级将一直使用到下一次扫描;在两次扫描之间出现的新用户会实时计算其优先级。

在FairCallQueue中,存在多个优先级队列,每个队列都被分配了一个权重。当请求到达调用队列时,会根据当前为该调用分配的优先级(由RpcScheduler决定)将请求放入其中一个优先级队列。当处理线程尝试从调用队列获取项目时,具体从哪个队列提取由RpcMultiplexer决定;目前这是硬编码为WeightedRoundRobinMultiplexer。WRRM根据队列的权重来服务请求;默认4个优先级级别的权重为(8, 4, 2, 1)。因此,WRRM会从最高优先级队列服务8个请求,次高优先级队列4个,第三优先级队列2个,最低优先级队列1个,然后再次从最高优先级队列服务8个请求,依此类推。

除了上述讨论的优先级加权机制外,还存在可配置的退避机制。在该机制下,服务器会向客户端抛出异常而非处理请求;客户端需要等待一段时间(例如通过指数退避算法)后重试。通常,当请求尝试放入已满的优先级队列(FCQ队列)时会触发退避机制。这有助于进一步限制高影响客户端,降低负载,并能带来显著效益。系统还具备响应时间退避功能:当高优先级队列的请求处理速度过慢时,将触发低优先级请求的退避机制。例如,若优先级1队列的响应时间阈值设为10秒,而该队列实际平均响应时间为12秒时,优先级2及以下的传入请求将收到退避异常,而优先级0和1的请求仍会正常处理。该设计旨在当系统整体负载过高影响到高优先级客户端时,强制让资源占用较大的客户端执行退避。

上述讨论在涉及如何对请求进行分组以实现限流时,指的是请求的用户。这可以通过身份提供者进行配置,默认使用UserIdentityProvider。用户身份提供者直接使用提交请求的客户端用户名。不过,也可以使用自定义身份提供者来基于其他分组执行限流,或集成外部身份提供者。

如果特定用户提交重要请求且您不希望限制他们,可以将他们设置为服务用户。这些用户始终会被调度到高优先级队列中,且不会计入普通用户调用的优先级计算。

基于成本的公平呼叫队列

尽管公平调用队列本身能很好地缓解提交大量请求的用户带来的影响,但它并未考虑每个请求的处理成本。因此,对于HDFS NameNode而言,提交1000次"getFileInfo"请求的用户,与在某个超大目录下提交1000次"listStatus"请求的用户,或提交1000次"mkdir"请求(这些操作成本更高,因为它们需要对命名空间获取排他锁)的用户,其优先级是相同的。为了在用户请求优先级排序时考虑操作的成本,公平调用队列增加了"基于成本"的扩展功能,该功能通过用户操作的总处理时间来确定其优先级。默认情况下,成本计算不考虑排队时间(等待处理的时间)和锁等待时间(等待获取锁的时间),无锁处理时间按中性权重(1倍)计算,共享锁处理时间权重提高10倍,排他锁处理时间权重提高100倍。这种机制试图根据用户对服务器造成的实际负载来优先处理请求。要启用此功能,请按如下说明将costprovder.impl配置设置为org.apache.hadoop.ipc.WeightedTimeCostProvider

配置

本节介绍如何配置公平呼叫队列。

配置前缀

所有与调用队列相关的配置仅适用于单个IPC服务器。这样可以使用单个配置文件来配置不同的组件,甚至组件内的不同IPC服务器,使它们拥有独立配置的调用队列。每个配置都以ipc.为前缀,其中是要配置的IPC服务器所使用的端口。例如,ipc.8020.callqueue.impl将调整运行在8020端口的IPC服务器的调用队列实现。在本节剩余部分,将省略此前缀。

完整配置列表

配置键 适用组件 描述 默认值
backoff.enable 通用 当队列满时是否启用客户端退避机制。 false
callqueue.impl General The fully qualified name of a class to use as the implementation of a call queue. Use org.apache.hadoop.ipc.FairCallQueue for the Fair Call Queue. java.util.concurrent.LinkedBlockingQueue (FIFO queue)
callqueue.capacity.weights General The capacity allocation weights among all subqueues. A postive int array whose length is equal to the scheduler.priority.levels is expected where each int is the relative weight out of total capacity. i.e. if a queue with capacity weight w, its queue capacity is capacity * w/sum(weights)
scheduler.impl General The fully qualified name of a class to use as the implementation of the scheduler. Use org.apache.hadoop.ipc.DecayRpcScheduler in conjunction with the Fair Call Queue. org.apache.hadoop.ipc.DefaultRpcScheduler (no-op scheduler)
If using FairCallQueue, defaults to org.apache.hadoop.ipc.DecayRpcScheduler
scheduler.priority.levels RpcScheduler, CallQueue 调度器和调用队列中使用的优先级级别数量。 4
faircallqueue.multiplexer.weights WeightedRoundRobinMultiplexer How much weight to give to each priority queue. This should be a comma-separated list of length equal to the number of priority levels. Weights descend by a factor of 2 (e.g., for 4 levels: 8,4,2,1)
identity-provider.impl DecayRpcScheduler 将用户请求映射到其身份的身份提供者。 org.apache.hadoop.ipc.UserIdentityProvider
cost-provider.impl DecayRpcScheduler The cost provider mapping user requests to their cost. To enable determination of cost based on processing time, use org.apache.hadoop.ipc.WeightedTimeCostProvider. org.apache.hadoop.ipc.DefaultCostProvider
decay-scheduler.period-ms DecayRpcScheduler 衰减因子应用于用户操作计数的频率。较高的值开销较小,但对客户端行为变化的响应较慢。 5000
decay-scheduler.decay-factor DecayRpcScheduler 在衰减用户操作计数时,应用的乘法衰减因子。数值越高,对历史操作的权重影响越大,本质上使调度器具有更长的记忆周期,并在更长时间内对高负载客户端进行惩罚。 0.5
decay-scheduler.thresholds DecayRpcScheduler The client load threshold, as an integer percentage, for each priority queue. Clients producing less load, as a percent of total operations, than specified at position i will be given priority i. This should be a comma-separated list of length equal to the number of priority levels minus 1 (the last is implicitly 100). Thresholds ascend by a factor of 2 (e.g., for 4 levels: 13,25,50)
decay-scheduler.backoff.responsetime.enable DecayRpcScheduler 是否启用基于响应时间的退避功能。 false
decay-scheduler.backoff.responsetime.thresholds DecayRpcScheduler The response time thresholds, as time durations, for each priority queue. If the average response time for a queue is above this threshold, backoff will occur in lower priority queues. This should be a comma-separated list of length equal to the number of priority levels. Threshold increases by 10s per level (e.g., for 4 levels: 10s,20s,30s,40s)
decay-scheduler.metrics.top.user.count DecayRpcScheduler 需要输出指标信息的顶级(即负载最重)用户数量。 10
decay-scheduler.service-users DecayRpcScheduler 服务用户将始终被调度到最高优先级队列中,且不会包含在普通用户调用的优先级计算中。它们以逗号分隔的列表形式指定。
weighted-cost.lockshared WeightedTimeCostProvider 应用于持有共享(读)锁的处理阶段时间消耗的权重乘数。 10
weighted-cost.lockexclusive WeightedTimeCostProvider 应用于持有独占(写)锁处理阶段所耗时间的权重乘数。 100
weighted-cost.{handler,lockfree,response} WeightedTimeCostProvider The weight multiplier to apply to the time spent in the processing phases which do not involve holding a lock. See org.apache.hadoop.ipc.ProcessingDetails.Timing for more details on each phase. 1

示例配置

这是一个配置IPC服务器在8020端口使用FairCallQueueDecayRpcScheduler且仅设置2个优先级级别的示例。最活跃的10%用户将受到严重惩罚,仅分配1%的总处理请求量。

<property>
     <name>ipc.8020.callqueue.impl</name>
     <value>org.apache.hadoop.ipc.FairCallQueue</value>
</property>
<property>
     <name>ipc.8020.callqueue.capacity.weights</name>
     <value>7,3</value>
</property>
<property>
     <name>ipc.8020.scheduler.impl</name>
     <value>org.apache.hadoop.ipc.DecayRpcScheduler</value>
</property>
<property>
     <name>ipc.8020.scheduler.priority.levels</name>
     <value>2</value>
</property>
<property>
     <name>ipc.8020.faircallqueue.multiplexer.weights</name>
     <value>99,1</value>
</property>
<property>
     <name>ipc.8020.decay-scheduler.thresholds</name>
     <value>90</value>
</property>