本文档介绍如何为 Hadoop 配置和管理公平调用队列。
确保已正确安装、配置和设置 Hadoop。有关更多信息,请参阅
Hadoop 服务器组件(特别是 HDFS NameNode)会遇到来自客户端的非常重的 RPC 负载。默认情况下,所有客户端请求都会通过先进先出队列进行路由,并按到达顺序提供服务。这意味着提交大量请求的单个用户可以轻松地使服务不堪重负,从而导致所有其他用户的服务质量下降。公平调用队列和相关组件旨在减轻这种影响。
IPC 堆栈中有一些组件具有复杂的相互作用,每个组件都有自己的调整参数。下图展示了它们交互的示意图,将在下面进行解释。
在以下说明中,加粗单词指代命名实体或可配置项。
当客户端向 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”请求的用户享有相同的优先级,而“mkdir”请求的成本更高,因为它们需要对名称系统进行独占锁定。为了在考虑用户请求的优先级时考虑操作的成本,公平调用队列有一个“基于成本”的扩展,它使用用户的操作的总处理时间来确定该用户的优先级。默认情况下,队列时间(等待处理的时间)和锁等待时间(等待获取锁的时间)不考虑在成本中,在没有锁的情况下处理所花费的时间以中性(1 倍)加权,在共享锁的情况下处理所花费的时间加权为 10 倍,在独占锁的情况下处理所花费的时间加权为 100 倍。这尝试根据用户对服务器造成的实际负载来确定其优先级。要启用此功能,请将 costprovder.impl
配置设置为 org.apache.hadoop.ipc.WeightedTimeCostProvider
,如下所述。
本节介绍如何配置公平调用队列。
所有与调用队列相关的配置仅与单个 IPC 服务器相关。这允许使用单个配置文件来配置不同的组件,甚至组件内的不同 IPC 服务器,以具有唯一配置的调用队列。每个配置都以 ipc.<port_number>
为前缀,其中 <port_number>
是要配置的 IPC 服务器使用的端口。例如,ipc.8020.callqueue.impl
将调整在端口 8020 上运行的 IPC 服务器的调用队列实现。在本节的其余部分中,将省略此前缀。
配置键 | 适用的组件 | 说明 | 默认值 |
---|---|---|---|
backoff.enable | 一般 | 当队列已满时是否启用客户端退避。 | false |
callqueue.impl | 一般 | 用作呼叫队列实现的类的完全限定名称。对于公平呼叫队列,使用 org.apache.hadoop.ipc.FairCallQueue 。 |
java.util.concurrent.LinkedBlockingQueue (FIFO 队列) |
scheduler.impl | 一般 | 用作调度程序实现的类的完全限定名称。结合公平呼叫队列,使用 org.apache.hadoop.ipc.DecayRpcScheduler 。 |
org.apache.hadoop.ipc.DefaultRpcScheduler (无操作调度程序)如果使用 FairCallQueue,则默认为 org.apache.hadoop.ipc.DecayRpcScheduler |
scheduler.priority.levels | RpcScheduler,CallQueue | 在调度程序和呼叫队列中使用的优先级级别数。 | 4 |
faircallqueue.multiplexer.weights | WeightedRoundRobinMultiplexer | 赋予每个优先级队列的权重。这应为一个逗号分隔的列表,其长度等于优先级级别数。 | 权重以 2 的倍数递减(例如,对于 4 个级别:8,4,2,1 ) |
identity-provider.impl | DecayRpcScheduler | 将用户请求映射到其身份的身份提供程序。 | org.apache.hadoop.ipc.UserIdentityProvider |
cost-provider.impl | DecayRpcScheduler | 将用户请求映射到其成本的成本提供程序。若要启用基于处理时间的成本确定,请使用 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 | 每个优先级队列的客户端负载阈值,以整数百分比表示。作为总操作的百分比,产生低于位置 i 处指定负载的客户端将被赋予优先级 i。这应为一个逗号分隔的列表,其长度等于优先级级别数减 1(最后一个隐式为 100)。 | 阈值以 2 的倍数递增(例如,对于 4 个级别:13,25,50 ) |
decay-scheduler.backoff.responsetime.enable | DecayRpcScheduler | 是否启用按响应时间退避功能。 | false |
decay-scheduler.backoff.responsetime.thresholds | DecayRpcScheduler | 每个优先级队列的响应时间阈值(作为时间持续时间)。如果队列的平均响应时间高于此阈值,则较低优先级队列中会出现退避。这应为长度等于优先级级别数的逗号分隔列表。 | 阈值每级增加 10 秒(例如,对于 4 级:10 秒、20 秒、30 秒、40 秒 ) |
decay-scheduler.metrics.top.user.count | DecayRpcScheduler | 要针对其发出指标信息的顶级(即最繁重的)用户数。 | 10 |
weighted-cost.lockshared | WeightedTimeCostProvider | 要应用于持有共享(读取)锁的处理阶段所花费时间的权重乘数。 | 10 |
weighted-cost.lockexclusive | WeightedTimeCostProvider | 要应用于持有独占(写入)锁的处理阶段所花费时间的权重乘数。 | 100 |
weighted-cost.{handler,lockfree,response} | WeightedTimeCostProvider | 要应用于不涉及持有锁的处理阶段所花费时间的权重乘数。有关每个阶段的更多详细信息,请参阅 org.apache.hadoop.ipc.ProcessingDetails.Timing 。 |
1 |
这是一个配置示例,其中端口 8020 上的 IPC 服务器使用 FairCallQueue
与 DecayRpcScheduler
且仅有 2 个优先级级别。最繁重的 10% 的用户受到严厉惩罚,仅给予处理的总请求的 1%。
<property> <name>ipc.8020.callqueue.impl</name> <value>org.apache.hadoop.ipc.FairCallQueue</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>