问题描述
在调用HDFS获取文件系统的get接口时,指定用户可能会导致OOM问题,示例代码如下:
FileSystem fileSystem = FileSystem.get(uri, conf, "hadoopuser");
问题溯源
该方法源码:
在有缓存的情况下将从Cache中取,一路取下去最后会从map中根据key取内容
但如果指定了user,此处根据key只能取到空值,
原因:
如下图所示,Cache的map的Key中包括了UserGroupInformation ugi,上述拿值时候会以此类型作为Key回到指定用户名时最初调用的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