hadoop2.7.3源码解析之hadoop RPC使用

来源:互联网 发布:海宁淘宝摄影 编辑:程序博客网 时间:2024/06/06 00:16

  • 概述
  • namenode提供服务
  • 客户端获取代理
  • 客户端具体的发送数据流程
    • ClientNamenodeProtocolPB序列化相应的方法
    • 发送序列化之后的数据
    • 服务端反序列化

概述

在以前的博客中,我简单的介绍了一下hadoop rpc框架的实现流程(http://blog.csdn.net/zhangjun5965/article/details/59653549),这一小节主要介绍一下在hdfs中,是如何运用这个rpc框架进行通讯的。

首先回顾一下使用hadoop rpc的主要流程

  1. 首先定义client和server交互的接口(如client和namenode的ClientProtocol)
  2. 服务器端实现接口(如namenode提供服务的NameNodeRpcServer类)
  3. 服务器端启动服务。(通过RPC.Builder(conf).build())
  4. 客户端获取接口的代理,执行相关的方法。

下面我们简单讲解一下rpc框架在hadoop的具体使用

namenode提供服务

namenode用于对外提供rpc服务的是NameNodeRpcServe类,这个类实现了NamenodeProtocols接口,NamenodeProtocols定义了NamenodeProtocols需要实现的所有的接口。
比如客户端和namenode交互的ClientProtocol、datanode和namenode交互的DatanodeProtocol,用于提供ha服务的HAServiceProtocol等。

/** The full set of RPC methods implemented by the Namenode.  */@InterfaceAudience.Privatepublic interface NamenodeProtocols  extends ClientProtocol,          DatanodeProtocol,          NamenodeProtocol,          RefreshAuthorizationPolicyProtocol,          RefreshUserMappingsProtocol,          RefreshCallQueueProtocol,          GenericRefreshProtocol,          GetUserMappingsProtocol,          HAServiceProtocol,          TraceAdminProtocol {}

此外,我们在这个类中看到他的内部定义了两个rpc服务,一个是针对客户端的,一个是针对namenode的

  /** The RPC server that listens to requests from DataNodes */  private final RPC.Server serviceRpcServer;  private final InetSocketAddress serviceRPCAddress;  /** The RPC server that listens to requests from clients */  protected final RPC.Server clientRpcServer;  protected final InetSocketAddress clientRpcAddress;

在构造方法中,实例化了这两个变量

      this.serviceRpcServer = new RPC.Builder(conf)          .setProtocol(              org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolPB.class)          .setInstance(clientNNPbService)          .setBindAddress(bindHost)          .setPort(serviceRpcAddr.getPort()).setNumHandlers(serviceHandlerCount)          .setVerbose(false)          .setSecretManager(namesystem.getDelegationTokenSecretManager())          .build();............................clientNNPbService).setBindAddress(bindHost)        .setPort(rpcAddr.getPort()).setNumHandlers(handlerCount)        .setVerbose(false)        .setSecretManager(namesystem.getDelegationTokenSecretManager()).build();

在start方法中启动了这两个服务。根据代码,发现是在namenode和BackupNode在启动的时候调用了start方法,也就是说这两个服务是在namenode启动的时候初始化的。

这里写图片描述

客户端获取代理

不管是创建文件,还是删除,都是先通过FileSystem.get(conf)来首先获取具体的的文件系统,FileSystem会根据传入的conf来进行适配,比如hdfs://开头的就实例化DistributedFileSystem,最终通过FileSystem.createFileSystem(URI, Configuration)来初始化具体的文件系统。

  private static FileSystem createFileSystem(URI uri, Configuration conf      ) throws IOException {    Class<?> clazz = getFileSystemClass(uri.getScheme(), conf);    FileSystem fs = (FileSystem)ReflectionUtils.newInstance(clazz, conf);    fs.initialize(uri, conf);    return fs;  }

对于DistributedFileSystem来说, fs.initialize(uri, conf);当然就是调用了
DistributedFileSystem.initialize(URI, Configuration)方法,在这里构造了一个DFSClient对象,客户端所有的操作都是通过它来完成的。

  @Override  public void initialize(URI uri, Configuration conf) throws IOException {    super.initialize(uri, conf);    setConf(conf);    String host = uri.getHost();    if (host == null) {      throw new IOException("Incomplete HDFS URI, no host: "+ uri);    }    homeDirPrefix = conf.get(        DFSConfigKeys.DFS_USER_HOME_DIR_PREFIX_KEY,        DFSConfigKeys.DFS_USER_HOME_DIR_PREFIX_DEFAULT);    this.dfs = new DFSClient(uri, conf, statistics);    this.uri = URI.create(uri.getScheme()+"://"+uri.getAuthority());    this.workingDir = getHomeDirectory();  }

在DFSClient的构造方法里,获取了namenode的代理对象,用于和namenode进行交互。

      Preconditions.checkArgument(nameNodeUri != null,          "null URI");      proxyInfo = NameNodeProxies.createProxy(conf, nameNodeUri,          ClientProtocol.class, nnFallbackToSimpleAuth);      this.dtService = proxyInfo.getDelegationTokenService();      this.namenode = proxyInfo.getProxy();

在createProxy方法里,通过查询配置文件获取是否配置了HA,来分别获取不同的代理,为了简单理解,我们现在只说一下非HA的情况。非HA的情况是调用了NameNodeProxies.createNonHAProxy(Configuration, InetSocketAddress, Class, UserGroupInformation, boolean, AtomicBoolean)来获取相应的代理。

在这,主要是针对不同的协议来分别获取不同的代理,

  public static <T> ProxyAndInfo<T> createNonHAProxy(      Configuration conf, InetSocketAddress nnAddr, Class<T> xface,      UserGroupInformation ugi, boolean withRetries,      AtomicBoolean fallbackToSimpleAuth) throws IOException {    Text dtService = SecurityUtil.buildTokenService(nnAddr);    T proxy;    if (xface == ClientProtocol.class) {      proxy = (T) createNNProxyWithClientProtocol(nnAddr, conf, ugi,          withRetries, fallbackToSimpleAuth);    } else if (xface == JournalProtocol.class) {      proxy = (T) createNNProxyWithJournalProtocol(nnAddr, conf, ugi);    } else if (xface == NamenodeProtocol.class) {      proxy = (T) createNNProxyWithNamenodeProtocol(nnAddr, conf, ugi,          withRetries);    } else if (xface == GetUserMappingsProtocol.class) {      proxy = (T) createNNProxyWithGetUserMappingsProtocol(nnAddr, conf, ugi);    } else if (xface == RefreshUserMappingsProtocol.class) {      proxy = (T) createNNProxyWithRefreshUserMappingsProtocol(nnAddr, conf, ugi);    } else if (xface == RefreshAuthorizationPolicyProtocol.class) {      proxy = (T) createNNProxyWithRefreshAuthorizationPolicyProtocol(nnAddr,          conf, ugi);    } else if (xface == RefreshCallQueueProtocol.class) {      proxy = (T) createNNProxyWithRefreshCallQueueProtocol(nnAddr, conf, ugi);    } else {      String message = "Unsupported protocol found when creating the proxy " +          "connection to NameNode: " +          ((xface != null) ? xface.getClass().getName() : "null");      LOG.error(message);      throw new IllegalStateException(message);    }    return new ProxyAndInfo<T>(proxy, dtService, nnAddr);  }

对于ClientProtocol接口来说,通过createNNProxyWithClientProtocol方法来获取,进入这个方法。

  private static ClientProtocol createNNProxyWithClientProtocol(      InetSocketAddress address, Configuration conf, UserGroupInformation ugi,      boolean withRetries, AtomicBoolean fallbackToSimpleAuth)      throws IOException {    RPC.setProtocolEngine(conf, ClientNamenodeProtocolPB.class, ProtobufRpcEngine.class);    final RetryPolicy defaultPolicy =         RetryUtils.getDefaultRetryPolicy(            conf,             DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_ENABLED_KEY,             DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_ENABLED_DEFAULT,             DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_SPEC_KEY,            DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_SPEC_DEFAULT,            SafeModeException.class);    final long version = RPC.getProtocolVersion(ClientNamenodeProtocolPB.class);    //首先获取了一个ClientNamenodeProtocolPB代理。    ClientNamenodeProtocolPB proxy = RPC.getProtocolProxy(        ClientNamenodeProtocolPB.class, version, address, ugi, conf,        NetUtils.getDefaultSocketFactory(conf),        org.apache.hadoop.ipc.Client.getTimeout(conf), defaultPolicy,        fallbackToSimpleAuth).getProxy();    //是否有重试机制    if (withRetries) { // create the proxy with retries      Map<String, RetryPolicy> methodNameToPolicyMap                  = new HashMap<String, RetryPolicy>();      ClientProtocol translatorProxy =        new ClientNamenodeProtocolTranslatorPB(proxy);      return (ClientProtocol) RetryProxy.create(          ClientProtocol.class,          new DefaultFailoverProxyProvider<ClientProtocol>(              ClientProtocol.class, translatorProxy),          methodNameToPolicyMap,          defaultPolicy);    } else {      return new ClientNamenodeProtocolTranslatorPB(proxy);    }  }

我们看到最后都是通过传入ClientNamenodeProtocolPB类型的参数proxy构造了一个ClientNamenodeProtocolTranslatorPB类型的接口代理。

client和namenode交互应该是ClientProtocol,可是为什么要中间再来一个ClientNamenodeProtocolPB接口呢,这就是下面我们要讲的东西了

客户端具体的发送数据流程

ClientNamenodeProtocolPB序列化相应的方法

client和namenode交互使用的是ClientProtocol接口,但是这个接口里面的方法的参数是java的类型,是无法在网络上传输的,所以需要进行序列化操作。所以就有了在客户端序列化操作的ClientNamenodeProtocolPB接口,ClientNamenodeProtocolPB采用了适配器模式对ClientProtocol对应的方法进行了一一的适配,将方法转换成可以在网络上传输的序列化之后的格式。

我们以delete方法为例,当调用了ClientProtocol的方法进行删除文件操作的时候,是先进入了oClientNamenodeProtocolTranslatorPB.delete(String, boolean)方法,用传进来的参数构造了一个DeleteRequestProto用于网络传输的对象。然后通过ClientNamenodeProtocolPB的delete方法来执行相应的操作。

发送序列化之后的数据

然后会在ClientNamenodeProtocolPB相应的代理的invoke方法里,通过client的call方法往服务器发送数据。

服务端反序列化

同样在服务器端也有一个用于反序列化的ClientNamenodeProtocolServerSideTranslatorPB类,用于将server接收的数据进行反序列化,然后在调用NameNodeRpcServer相应的方法执行相应的方法。

原创粉丝点击