hadoop.fs.FileSystem.get导致OOM的原因和解决方案

问题描述

在调用HDFS获取文件系统的get接口时,指定用户可能会导致OOM问题,示例代码如下:

FileSystem fileSystem = FileSystem.get(uri, conf, "hadoopuser");

问题溯源

该方法源码:
hadoop.fs.FileSystem.get导致OOM的原因和解决方案_第1张图片
在有缓存的情况下将从Cache中取,一路取下去最后会从map中根据key取内容
hadoop.fs.FileSystem.get导致OOM的原因和解决方案_第2张图片
hadoop.fs.FileSystem.get导致OOM的原因和解决方案_第3张图片
hadoop.fs.FileSystem.get导致OOM的原因和解决方案_第4张图片
但如果指定了user,此处根据key只能取到空值,
原因:
如下图所示,Cache的map的Key中包括了UserGroupInformation ugi,上述拿值时候会以此类型作为Keyhadoop.fs.FileSystem.get导致OOM的原因和解决方案_第5张图片回到指定用户名时最初调用的get方法,源码如下:

  public static FileSystem get(final URI uri, final Configuration conf,
        final String user) throws IOException, InterruptedException {
    String ticketCachePath =
      conf.get(CommonConfigurationKeys.KERBEROS_TICKET_CACHE_PATH);
    //进入getBestUGI查看一下源码
    UserGroupInformation ugi =
        UserGroupInformation.getBestUGI(ticketCachePath, user);
    return ugi.doAs(new PrivilegedExceptionAction<FileSystem>() {
      @Override
      public FileSystem run() throws IOException {
        return get(uri, conf);
      }
    });
  }

getBestUGI查看源码:

  public static UserGroupInformation getBestUGI(
      String ticketCachePath, String user) throws IOException {
    if (ticketCachePath != null) {
      return getUGIFromTicketCache(ticketCachePath, user);
    } else if (user == null) {
      return getCurrentUser();
    } else {
    //注意这一句,查看一下源码
      return createRemoteUser(user);
    }    
  }

createRemoteUser源码

@InterfaceAudience.Public
  @InterfaceStability.Evolving
  public static UserGroupInformation createRemoteUser(String user, AuthMethod authMethod) {
    if (user == null || user.isEmpty()) {
      throw new IllegalArgumentException("Null user");
    }
    //此处subject一直是新的
    Subject subject = new Subject();
    subject.getPrincipals().add(new User(user));
    UserGroupInformation result = new UserGroupInformation(subject);
    result.setAuthenticationMethod(authMethod);
    return result;
  }

可以看出,在指定了用户名的情况下一直会新建subject,那么如何判断两个key是否相等呢?查看一下Cache重写的hashCode方法:

 public int hashCode() {
        return (scheme + authority).hashCode() + ugi.hashCode() + (int)unique;
      }

可以看出在判断两个Key值是否一致时,会判断ugi的hashCode是否一致,其中ugi.hashCode代码如下,又能够看出判断ugi的hashCode是否一致会去判断subject的hashCode是否一致,而上面说过的,调用createRemoteUser每次都会新建一个subject,这也就导致了这两个Key值永远是不同的,用这个Key值取也一定是取不到值的:

public int hashCode() {
    return System.identityHashCode(subject);
  }

在取不到值的情况下,getInternal中的下列语句就会一直被执行到

 map.put(key, fs);

最终导致OOM

解决方法

自己定义一个map缓存,调用FileSystem.newInstance,不使用FileSystem的cache

你可能感兴趣的:(日常解决小问题,hadoop,java,大数据)