Hadoop源码分析之二(RPC机制之Call处理)

上一篇介绍整个RPC Server端的处理机制。http://www.linuxidc.com/Linux/2013-01/77251.htm

下面介绍在整个处理机制中怎么把具体的Request Call转换并调用到整体的实现逻辑。

主要以NameNode Client PRC Server作为例子来说明,整个转换通过Google Protocol Buffer RPC来实现。

          final Call call = callQueue.take(); // pop the queue; maybe blocked here
        .....
          CurCall.set(call);
          try {
            // Make the call as the user via Subject.doAs, thus associating the call with the Subject
            if (call.connection.user == null) {
              value = call(call.rpcKind, call.connection.protocolName, call.rpcRequest, 
                          call.timestamp);
            } else {
              value = 
                call.connection.user.doAs
                  (new PrivilegedExceptionAction<Writable>() {
                    @Override
                    public Writable run() throws Exception {
                      // make the call
                      return call(call.rpcKind, call.connection.protocolName, 
                                  call.rpcRequest, call.timestamp);

                    }
                  }
                  );
            }
          } catch (Throwable e) {
            //process exception
          }
          CurCall.set(null);
          synchronized (call.connection.responseQueue) {
            // setupResponse() needs to be sync'ed together with responder.doResponse() since setupResponse may use
            // SASL to encrypt response data and SASL enforces
            // its own message ordering.
            setupResponse(buf, call, (error == null) ? RpcStatusProto.SUCCESS
                : RpcStatusProto.ERROR, value, errorClass, error);
            
            // Discard the large buf and reset it back to smaller size to free up heap
            if (buf.size() > maxRespSize) {
              LOG.warn("Large response size " + buf.size() + " for call "
                  + call.toString());
              buf = new ByteArrayOutputStream(INITIAL_RESP_BUF_SIZE);
            }
            responder.doRespond(call);
          }

从上面可以看出从call queue中取出一个call,调用call方法来处理call的逻辑,处理之后把call的结果封装成response并通过responder的doRespond把call的response加到response queue中。那行call方法是怎么处理具体call的逻辑的呢,下面就来介绍,具体call方法的逻辑在RPC中实现具体如下:

 public Writable call(RPC.RpcKind rpcKind, String protocol,
        Writable rpcRequest, long receiveTime) throws Exception {
      return getRpcInvoker(rpcKind).call(this, protocol, rpcRequest,
          receiveTime);
 }

根据rpcKind来取到一个RPC Invoker实现,当然这里的Kind的Protocol Buffer,因此具体的实现在ProtobufRpcEngine中实现,具体如下:

public Writable call(RPC.Server server, String protocol,
          Writable writableRequest, long receiveTime) throws Exception {
//get RPC Request
        RpcRequestWritable request = (RpcRequestWritable) writableRequest;
        HadoopRpcRequestProto rpcRequest = request.message;
        String methodName = rpcRequest.getMethodName();//get need to invoke method name
        String protoName = rpcRequest.getDeclaringClassProtocolName();
        long clientVersion = rpcRequest.getClientProtocolVersion();
      
        ProtoClassProtoImpl protocolImpl = getProtocolImpl(server, protoName,
            clientVersion);
        BlockingService service = (BlockingService) protocolImpl.protocolImpl;
        MethodDescriptor methodDescriptor = service.getDescriptorForType()
            .findMethodByName(methodName);//find method define
        if (methodDescriptor == null) {
          String msg = "Unknown method " + methodName + " called on " + protocol
              + " protocol.";
          LOG.warn(msg);
          throw new RpcServerException(msg);
        }
        Message prototype = service.getRequestPrototype(methodDescriptor);
        Message param = prototype.newBuilderForType()
            .mergeFrom(rpcRequest.getRequest()).build();//build parameter message
        Message result;
        try {
          long startTime = Time.now();
          server.rpcDetailedMetrics.init(protocolImpl.protocolClass);
    //call method
          result = service.callBlockingMethod(methodDescriptor, null, param);
        //do some metrics...
        } catch (ServiceException e) {
          throw (Exception) e.getCause();
        } catch (Exception e) {
          throw e;
        }
        return new RpcResponseWritable(result);
      }

    }

通过上面的可以看到通过request得到需要调用的method name和parameter,同时可以发现所有的request和response都实现了protobuf的message,因此可以很方便的完成序列化和反序列化。通过调用callBlockingMethod方法来调用具体的方法。

那么怎么通过callBlaockingMethod转到把具体的Method呢,这里以NameNode的Client RPC Server说明。在org.apache.Hadoop.hdfs.server.namenode.NameNodeRpcServer的构造函数中有如下实现:

    //service transfer implement
    ClientNamenodeProtocolServerSideTranslatorPB 
      clientProtocolServerTranslator = 
        new ClientNamenodeProtocolServerSideTranslatorPB(this);
  //build blocking service instance
    BlockingService clientNNPbService = ClientNamenodeProtocol.
        newReflectiveBlockingService(clientProtocolServerTranslator);
  ......
    // Add all the RPC protocols that the namenode implements, get RPC Server Instance
    this.clientRpcServer = RPC.getServer(
        org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolPB.class, 
        clientNNPbService, socAddr.getHostName(),
            socAddr.getPort(), handlerCount, false, conf,
            namesystem.getDelegationTokenSecretManager());

上面代码主要做了3件事:

 

 

  1. 创建一个Service Transfer的实例,在这个类中实现了Protocal RPC Service转化成具体的具体,可以看到把当前NameNodeRpcServer的实例传递给了ClientNamenodeProtocolServerSideTranslatorPB构造函数;
  2. 通过ClientNamenodeProtocolServerSideTranslatorPB实现创建一个BlockingService的实例;
  3. 通过BlockingService实现构建一个RPC Server的实例。
因此所有的转换工作在ClientNamenodeProtocolServerSideTranslatorPB中实现,需具体Call的实现在NameNodeRpcServer完成。
接下来我们看一下ClientNamenodeProtocolServerSideTranslatorPB是怎么实现的。
  1. 首先ClientNamenodeProtocolServerSideTranslatorPB实现了ClientNamenodeProtocolPB接口,需ClientNamenodeProtocolPB接口又继承了ClientNamenodeProtocol.BlockingInterface接口,而ClientNamenodeProtocol.BlockingInterface接口定义了所有RPC方法的定义,因此ClientNamenodeProtocolServerSideTranslatorPB其实就是RPC方法的具体实现;
  2. ClientNamenodeProtocolServerSideTranslatorPB的构建函数接受了NameNodeRpcServer的实现,因此可以发现具体RPC方法的实现其实就是调用了NameNodeRpcServer中的方法,并把结果封装成Response返回;
到这里整个RPC Server的实现就介绍完了,下面再简单的总结一下:
  1. 通过Google Protocol Buffer对Request/Response进行序列化和反序列化;
  2. 通过Java NIO把byte data从Client传输到Server端;
  3. Server再通过RPC转换到具体的实现。这里有一点奇怪,hadoop没有直接使用rpc来实现client-server之间的通信。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值