记一次namenode关机导致的问题

作者:李佩京
时间:2018-12-25


问题:

某日需要升级namenode系统,对一台namenode进行关机操作,但是关机后出现hive归档延迟。将之间关闭的机器开机后恢复正常。经过排查问题,关机的机器位于配置文件(hdfs-site.xml)中ns1的位置。

原因:

spark和hive默认开启缓存机制,但因为某些原因实际使用中关闭了缓存,导致高可用Namenode ns1机器不能关机,关机会早成数据流计算延迟。

具体原因:

首先为什么关闭缓存:

  1. 复用FileSystem对象,比如spark要去连hdfs找那些文件,比如查一个表的时候,如果这个表有10个文件,不关闭缓存,它会去连10次namenode所以将该功能关闭。如果打开了缓存的话,就会直接从静态缓存对象中返回已经创建的实例。而缓存默认是打开的。缓存的方式就是常见的懒加载模式(存在就返回,不存在就创建并放在 cache 中)。
  2. 为了防止非加密模式下的内存泄露,通过设置下面的参数为true禁用文件系统的缓存org.apache.hadoop.fs.FileSystem。

其次业务代码BUG:
业务代码中因为缓存未生效,会不断请求HDFS集群获取用户组信息,每次都会先请求配置文件中ns1的机器。当ns1关机后,程序会等待ns1返回信息,等待超时后才会连接ns2。同理hive和spark缓存机制未开启,导致每次请求都会经过ns1,最终导致数据流计算明显延迟。

解决方案

修改hive和spark配置,增加缓存机制,修改程序代码中的缓存机制。

详细操作步骤

修改配置文件(默认该两项为false状态)

<property>
<name>fs.hdfs.impl.disable.cache</name>
<value>false</value>
</property>
<property>
<name>fs.file.impl.disable.cache</name>
<value>false</value>
</property>

在hive-site.xml文件中增加缓存功能(默认是开启状态),spark和hive的相关配置都得修改并重启相关服务。

修改业务代码

/**
* 配置缓存
*/
private static Cache<String, UserGroupInformation> ugis = CacheBuilder.newBuilder().build();
public UserGroupInformation getUserGroupInformation(String userName) {
boolean isKerberosEnabled = configLoaderServer.getKerberosConfig().isKdcEnabled();
if (isKerberosEnabled) {
//Kerberos authentication
log.info(“Kerberos authentication isKdcEnabled=true”);
return UserGroupInformation.createProxyUser(userName, hadoopKerberosService.getUserGroupInformation(LoginPrincipalType.HDFS));
} else {
log.info(“SIMPLE authentication”);
UserGroupInformation ugi = null;
try {
ugi = ugis.get(userName, () -> UserGroupInformation.createRemoteUser(userName));
} catch (ExecutionException e) {
e.printStackTrace();
log.error(e);
}
return ugi;
//return UserGroupInformation.createRemoteUser(userName); //SIMPLE authentication
}
}
/**
* 注释下面closeFileSystem代码。
*/
@Override
public void closeFileSystem(FileSystem fs) {
//TODO 暂时停止closeFileSystem,因为会使缓存失效
// try {
// if (fs != null)
// fs.close();
// } catch (IOException e) {
// log.error(e.getMessage(), e);
// }
}

问题总结

  1. 当Spark和HDFS api同时存在时,当你的hdfs api去调用FileSystem的close方法时,会报java.io.IOException: Filesystem closed,因为他们都是去cache中获取连接,而你的api close了fileSystem,导致spark的hdfs连接不能用,所以会报错。
  2. hadoop运行mapreduce任务时,由于多个datanode节点需要读取hdfs filesystem,此时,如果一个节点因为网络或者其他原因关掉了该filesystem,而其他节点仍然使用的cache中的filesystem,导致触发IOException,出现这个问题是在使用hive的时候。
  3. 缓存
    FileSystem类中有一个Cache内部类,用于缓存已经被实例化的FileSystem。注意这个跟连接池还是有区别的,Cache中的缓存只是一个map,可以被多个线程拿到。这就会有一个问题,当你多线程同时get FileSystem的时候,可能返回的是同一个对象。所以切记,在多线程场景中,不要随意调用FileSystem.close,你关的连接可能会影响到其他正在使用的线程。
  4. 业务代码中getUserGroupInformation函数会在获取ThriftHive操作的client对象、清空表的指定分区下内容用于overwrite、列出分区目录等功能中使用,如果不添加缓存机制,每次会从hdfs-site.xml文件中读取第一个namenode,如果ns1是关机状态会导致程序等待一个ssh超时时间(默认为900s)。再连接ns2,导致的结果是数据计算服务明显卡顿。

注意: 当你在其他框架上拿fileSystem对象需要额外注意,例如在spark上进行 FileSystem.get(),如果你想自定义某些配置,设置hdfs的副本数(dfs.replication) 之类,你必须在configuration中关闭FileSystem的缓存机制。hadoop的FileSystem实例底层默认是复用的,所以如果执行了两次fileSystem.close(),第二次会报错FileSystem Already Closed异常(即使表面上是对两个实例执行的),一个典型的场景是同时使用Spark和Hadoop-Api,Spark会创建FileSystem实例,Hadoop-Api也会创建,由于底层复用,两者其实是同一个。因为关闭钩子的存在,应用退出时会执行两次FileSystem.close(),导致报错。解决这个问题的办法是在hdfs-site.xml增加以下配置,关闭FileSystem实例。

问题解决了,我们看一下hive是什么?
Hive简介:

  • Hive是一个构建于Hadoop顶层的数据仓库工具,可以查询和管理PB级别的分布式数据。
  • 支持大规模数据存储、分析,具有良好的可扩展性
  • 某种程度上可以看作是用户编程接口,本身不存储和处理数据。
  • 依赖分布式文件系统HDFS存储数据。
  • 依赖分布式并行计算模型MapReduce处理数据。
  • 定义了简单的类似SQL 的查询语言——HiveQL。
  • 用户可以通过编写的HiveQL语句运行MapReduce任务。
  • 可以很容易把原来构建在关系数据库上的数据仓库应用程序移植到Hadoop平台上。
  • 是一个可以提供有效、合理、直观组织和使用数据的分析工具。
    Hive分为三个角色:HiveServer、MetaStore、WebHcat。
  • HiveServer:将用户提交的HQL语句进行编译,解析成对应的Yarn任务,Spark任务或者HDFS操作,从而完成数据的提取,转换,分析。
  • MetaStroe:提供元数据服务。
  • WebHcat:对外提供基于Htpps洗衣的元数据访问、DDL查询等服务。
发布了48 篇原创文章 · 获赞 0 · 访问量 1283

猜你喜欢

转载自blog.csdn.net/zhinengyunwei/article/details/103976211