当前位置:   article > 正文

HDFS Standby NameNode Read功能剖析_hdfs 写数据 如果namenode处于standby状态可以发起写操作吗

hdfs 写数据 如果namenode处于standby状态可以发起写操作吗

前言


HDFS有着一套十分成熟的HA的机制来保证其服务的高可用性。在HA模式下,分别对应有Active和Standby NameNode的服务。Active NameNode用于提供对外数据服务,而Standby NameNode则负责做checkpoint的工作以及随时准备接替变成Active NameNode的角色,假设说当前Active NameNode意外不可用的情况发生的话。其实,Standby NameNode日常的工作并不多,除了定期checkpoint和准实时地同步元数据信息外,它并不处理来自外部client发起的读写请求,所以Standby NameNode服务的一个负载是比较低的。当Active NameNode的服务压力越来越大的时候,那么是否我们可以让Standby的NameNode去分流一部分的读压力呢?Hadoop社区在很早的时候就已经提出过此设想并且实现了这个功能。本文笔者将结合部分代码来简单分析分析HDFS Standby Read的实现原理。

HDFS Standby Read的背景及功能要求


首先我们来说说HDFS Standby Read的背景及功能要求。在Active NameNode随着集群规模的不断扩张下,其服务压力将会越来越大的。对于这种情况下,我们一般的做法是通过组建HDFS Federation的方式来达到服务横向扩展的目标。但是这种方式并没有在NameNode本身的服务能力上做更进一步的挖掘优化,而HDFS Standby Read的功能就是在这块的一个大大的补强。

在Standby Read模式下,HDFS原有的写请求依然是被Active NameNode所处理。Standby服务只是支持了读操作的处理,所以这里不会涉及到NameNode主要逻辑上的大改。不过这里面最需要解决的问题是,Standby一致性读的问题。

我们知道Standby NameNode是通过读取JournalNode上的editlog,从而进行transaction的同步的。在Active NameNode写editlog到出去,再到Standby NameNode去读这这批editlog,中间是存在时间gap的。所以在实现Standby Read功能时,我们并不是简简单单地把读请求直接转向Standby NN就完事了,这里会涉及到transaction的等待同步问题。下面笔者会详细介绍这块社区是怎么做的。

Standby NameNode一致性读的控制实现


原理分析


鉴于上小节提到的Standby NameNode状态同步的问题,需要Standby NameNode达到client最近一次的txid后,才能允许其处理client的读请求操作。

上面这句话什么意思呢?对于client而言,在它发起RPC请求时,Active NameNode和Standby NameNode各自有自身当前的txid,且Active NameNode的txid肯定要大于Standby的txid。这里我们标记Active txid为ann txid,Standby为 snn txid。如果这时,client发起后续请求到Active服务,那没有什么数据延时的问题,Active一直都是最新的状态。但是假设我们想让Standby NameNode也能够处理client的请求,那么它至少得达到刚刚client发起RPC时刻起时Active NameNode的txid的状态,即snn txid也达到ann txid值。

此部分过程简单阐述如下所示:

1)Client发起RPC请求前获取到当前Active NameNode的txid值,这里我们叫做lastSeenTxid。
2)随后Client发起读请求到Standby NameNode,在此请求中会带上上步骤的lastSeenTxid的值。
3)Standby NameNode在处理上步骤的RPC请求时,会比较自身当前的txid是否已经达到client的lastSeenTxid值,如果已经达到,则正常处理这个请求,否则将请求重新插入RPC callqueu等待下次被处理。这里的请求重新进queue的操作对于client来说,意味着这个RPC call还没有处理结束。

为了避免Client可能出现长时间等待Standby NameNode达到lastSeenTxid状态的情况,社区在Standby NameNode editlog的同步上做了一部分改进,包括支持editlog_inprogress里的transaction读取以及editlog信息的内存读取等等。

后面笔者来结合实际代码,来对应分析上面的过程。

代码分析


社区在实现的过程里定义了2个类来存放Client和Server端自身能够“看到”的txid值。

Client对应的类叫做ClientGSIContext,Server端(即NameNode)的叫做GlobalStateIdContext。

我们先来说说Client端的这个类,ClientGSIContext。ClientGSIContext类内部维护有lastSeenStateId这个值,代码如下所示:

/**
 * Global State Id context for the client.
 * <p>
 * This is the client side implementation responsible for receiving
 * state alignment info from server(s).
 */
@InterfaceAudience.Private
@InterfaceStability.Evolving
public class ClientGSIContext implements AlignmentContext {
   

  private final LongAccumulator lastSeenStateId =
      new LongAccumulator(Math::max, Long.MIN_VALUE);
  ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

lastSeenStateId这个值的更新和获取主要发生在接收到RPC response阶段(更新当前的lastSeenStateId值)和RPC请求发送(设置当前的lastSeenStateId值)的时候,代码如下,还是在这个类的逻辑里。

  /**
   * Client接收到请求response时更新当前的lastSeenStateId值。
   */
  @Override
  public void receiveResponseState(RpcResponseHeaderProto header) {
   
    lastSeenStateId.accumulate(header.getStateId());
  }

  /**
   * Client发起请求时设置当前的lastSeenStateId值信息到RPC请求里。
   */
  @Override
  public void updateRequestState(RpcRequestHeaderProto.Builder header) {
   
    header.setStateId(lastSeenStateId.longValue());
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

然后我们再来看Server端的GlobalStateIdContext是怎么处理的。首先是GlobalStateIdContext的类定义:

/**
 * This is the server side implementation responsible for passing
 * state alignment info to clients.
 */
@InterfaceAudience.Private
@InterfaceStability.Evolving
class GlobalStateIdContext implements AlignmentContext {
   
  /**
   * Estimated number of journal transactions a typical NameNode can execute
   * per second. The number is used to estimate how long a client's
   * RPC request will wait in the call queue before the Observer catches up
   * with its state id.
   */
  private static final long ESTIMATED_TRANSACTIONS_PER_SECOND = 10000L;

  /**
   * The client wait time on an RPC request is composed of
   * the server execution time plus the com
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号