HBase的Nonce实现分析

本文探讨了在处理RPC请求时,服务器响应超时导致的数据操作冲突问题,以及HBase通过Nonce管理机制解决该问题的方法。具体而言,当客户端请求超时时,会重复发送带有相同Nonce的请求,服务端通过NonceGroup和Nonce判断是否为同一操作,从而决定等待、拒绝还是直接处理,避免了重复处理和数据冲突。

        一、问题背景

        当客户端发送RPC请求给服务端时,基于各种原因,服务器的响应很可能会超时。如果客户端只是在那等待,针对数据的操作,很可能出现服务器端已处理完毕,但是无法通知客户端,此时,客户端只能重新发起请求,可是又可能造成服务端重复处理请求。该如何解决该问题呢?

        二、解决方案

        实际上,客户端发送RPC请求给服务器后,如果响应超时,那么客户端会重复发送请求,直到达到参数配置的重试次数上限。而且,客户端第一次发送和以后重发请求时,会附带相同的nonce,服务端只要根据nonce进行判断,就能得知是否为同一请求,并根据之前请求处理的结果,决定是等待、拒绝还是直接处理。

        三、HBase如何实现的

        在HRegionServer中,有一个ServerNonceManager类型的成员变量nonceManager,由它负责管理该RegionServer上的nonce。其定义如下:

        

final ServerNonceManager nonceManager;

        ServerNonceManager中有一个十分重要的方法,用于当一个操作在服务端执行后未及时反馈响应给客户端,客户端重新发起携带相同nonceGroup和nonce的同一操作的请求时,服务端根据nonceGroup和nonce做相应的判断。定义如下:

/**
   * Starts the operation if operation with such nonce has not already succeeded. If the
   * operation is in progress, waits for it to end and checks whether it has succeeded.
   * 
   * 如果操作未执行成功,重新开始一个操作。如果该操作在进行过程中,等待它完成并判断它是否成功。
   * @param group Nonce group.
   * @param nonce Nonce.
   * @param stoppable Stoppable that terminates waiting (if any) when the server is stopped.
   * @return true if the operation has not already succeeded and can proceed; false otherwise.
   */
  public boolean startOperation(long group, long nonce, Stoppable stoppable)
      throws InterruptedException {
	// 如果传入的nonce为0,则返回true,表明操作可以进行
    if (nonce == HConstants.NO_NONCE) return true;
    
    // 构造NonceKey实例nk
    NonceKey nk = new NonceKey(group, nonce);
    // 构造OperationContext实例ctx,初始状态为WAIT
    OperationContext ctx = new OperationContext();
    while (true) {
      // 将NonceKey到OperationContext的映射,添加到ConcurrentHashMap类型的nonces中去
      OperationContext oldResult = nonces.putIfAbsent(nk, ctx);
      // 如果之前没有,则说明该操作可以直接执行
      if (oldResult == null) return true;

      // Collision with some operation - should be extremely rare.
      // 如果之前存在该操作,则取出该操作nonce对应的OperationContext
      synchronized (oldResult) {
    	// 获得该nonce对应的OperationContext状态
        int oldState = oldResult.getState();
        LOG.debug("Conflict detected by nonce: " + nk + ", " + oldResult);
        // 如果之前的状态不是WAIT
        if (oldState != OperationContext.WAIT) {
          // 如果之前的状态是PROCEED,说明之前的操作执行完成且以失败告终,此处返回true,表示操作可以再次执行
          return oldState == OperationContext.PROCEED; // operation ended
        }
        
        // 等待一段时间后继续循环
        oldResult.setHasWait();
        oldResult.wait(this.conflictWaitIterationMs); // operation is still active... wait and loop
        
        // 判断RegionServer的状态
        if (stoppable.isStopped()) {
          throw new InterruptedException("Server stopped");
        }
      }
    }
  }
        在RSRpcServices的append()方法中,有如下代码:

if (r == null) {
      long nonce = startNonceOperation(m, nonceGroup);
      boolean success = false;
      try {
        r = region.append(append, nonceGroup, nonce);
        success = true;
      } finally {
        endNonceOperation(m, nonceGroup, success);
      }
      if (region.getCoprocessorHost() != null) {
        region.getCoprocessorHost().postAppend(append, r);
      }
    }
        其中,startNonceOperation()方法源码如下:

/**
   * Starts the nonce operation for a mutation, if needed.
   * 
   * 如果需要的话,为mutation开启一个nonce操作
   * 
   * @param mutation Mutation.
   * @param nonceGroup Nonce group from the request.
   * @returns Nonce used (can be NO_NONCE).
   */
  private long startNonceOperation(final MutationProto mutation, long nonceGroup)
      throws IOException, OperationConflictException {
	
	// 如果RegionServer上的nonceManager为null,或者该mutation不存在nonce,那么直接返回HConstants.NO_NONCE,即0
    if (regionServer.nonceManager == null || !mutation.hasNonce()) return HConstants.NO_NONCE;
    // 标志位,是否可以运行
    boolean canProceed = false;
    try {
      // 调用RegionServer上nonceManager的startOperation()方法,确定是否可以执行该操作
      canProceed = regionServer.nonceManager.startOperation(
        nonceGroup, mutation.getNonce(), regionServer);
    } catch (InterruptedException ex) {
      throw new InterruptedIOException("Nonce start operation interrupted");
    }
    
    if (!canProceed) {// 如果不能运行,抛出OperationConflictException异常,即操作冲突异常
      // TODO: instead, we could convert append/increment to get w/mvcc
      String message = "The operation with nonce {" + nonceGroup + ", " + mutation.getNonce()
        + "} on row [" + Bytes.toString(mutation.getRow().toByteArray())
        + "] may have already completed";
      throw new OperationConflictException(message);
    }
    
    // 最后,返回mutation的nonce
    return mutation.getNonce();
  }

        它会调用RegionServer上nonceManager的startOperation()方法,确定是否可以执行该操作。


计及风电并网运行的微电网及集群电动汽车综合需求侧响应的优化调度策略研究(Matlab代码实现)内容概要:本文研究了计及风电并网运行的微电网及集群电动汽车综合需求侧响应的优化调度策略,并提供了基于Matlab的代码实现。研究聚焦于在高渗透率可再生能源接入背景下,如何协调微电网内部分布式电源、储能系统与大规模电动汽车充电负荷之间的互动关系,通过引入需求侧响应机制,建立多目标优化调度模型,实现系统运行成本最小化、可再生能源消纳最大化以及电网负荷曲线的削峰填谷。文中详细阐述了风电出力不确定性处理、电动汽车集群充放电行为建模、电价型与激励型需求响应机制设计以及优化求解算法的应用。; 适合人群:具备一定电力系统基础知识和Matlab编程能力的研究生、科研人员及从事新能源、微电网、电动汽车等领域技术研发的工程师。; 使用场景及目标:①用于复现相关硕士论文研究成果,深入理解含高比例风电的微电网优化调度建模方法;②为开展电动汽车参与电网互动(V2G)、需求侧响应等课题提供仿真平台和技术参考;③适用于电力系统优化、能源互联网、综合能源系统等相关领域的教学与科研项目开发。; 阅读建议:建议读者结合文中提供的Matlab代码进行实践操作,重点关注模型构建逻辑与算法实现细节,同时可参考文档中提及的其他相关案例(如储能优化、负荷预测等),以拓宽研究视野并促进交叉创新。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值