一次flink任务重试失败的问题分析

【背景】

在研究flink任务失败重试的过程中,遇到了一个问题,具体表现为:在任务重试时,出现与NN连接失败,触发新的一次重试,然后重复此流程,直到达到重试上限后,任务失败退出。

本文就总结下整个问题的分析过程,以及涉及到的相关知识点。

【问题分析过程】

首先查看了任务的日志,发现有如下关键信息:

INFO org.apache.hadoop.io.retry.RetryInvocationHandler [] - java.io.IOException: org.apache.flink.shaded.hadoop2.com.google.protobuf.ServiceException: java.lang.IllegalStateException: Trying to access closed classloader. Please check if you store classloaders directly or indirectly in static fields. If the stacktrace suggests that the leak occurs in a third party library and cannot be fixed immediately, you can disable this check with the configuration 'classloader.check-leaked-classloader'., while invoking ClientNamenodeProtocolTranslatorPB.getFileInfo over hadoop-namenode-1.hadoop-namenode.dev-env-3.svc.cluster.local/192.168.141.164:9000. Trying to failover immediately.
Caused by: java.lang.IllegalArgumentException: Failed to specify server's Kerberos principal name
  at org.apache.hadoop.security.SaslRpcClient.getServerPrincipal(SaslRpcClient.java:325) ~[flink-shaded-hadoop-2-uber-2.10.1.jar:2.10.1]
  at org.apache.hadoop.security.SaslRpcClient.createSaslClient(SaslRpcClient.java:231) ~[flink-shaded-hadoop-2-uber-2.10.1.jar:2.10.1]
  at org.apache.hadoop.security.SaslRpcClient.selectSaslClient(SaslRpcClient.java:159) ~[flink-shaded-hadoop-2-uber-2.10.1.jar:2.10.1]
  at org.apache.hadoop.security.SaslRpcClient.saslConnect(SaslRpcClient.java:390) ~[flink-shaded-hadoop-2-uber-2.10.1.jar:2.10.1]
  at org.apache.hadoop.ipc.Client$Connection.setupSaslConnection(Client.java:617) ~[flink-shaded-hadoop-2-uber-2.10.1.jar:2.10.1]
  at org.apache.hadoop.ipc.Client$Connection.access$2200(Client.java:423) ~[flink-shaded-hadoop-2-uber-2.10.1.jar:2.10.1]
  at org.apache.hadoop.ipc.Client$Connection$2.run(Client.java:823) ~[flink-shaded-hadoop-2-uber-2.10.1.jar:2.10.1]
  at org.apache.hadoop.ipc.Client$Connection$2.run(Client.java:819) ~[flink-shaded-hadoop-2-uber-2.10.1.jar:2.10.1]
  at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_342]
  at javax.security.auth.Subject.doAs(Subject.java:422) ~[?:1.8.0_342]
  at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:2012) ~[flink-shaded-hadoop-2-uber-2.10.1.jar:2.10.1]
  at org.apache.hadoop.ipc.Client$Connection.setupIOstreams(Client.java:819) ~[flink-shaded-hadoop-2-uber-2.10.1.jar:2.10.1]
  ... 56 more

从这个日志里,可以发现有两个问题:

1)与nn连接失败是因为抛出了一个异常:无效的服务端principal。

2)存在一个IO异常的日志信息:访问已经关闭的classLoader。

对于第一个问题,根据其堆栈信息,可以快速找到其源码,并推测应该是从configurtaion类对象实例中没有正确获取到服务端的key,从而构造了一个空的principal,导致出现无效参数的异常。

再次复现问题,并进行debug跟踪了下,发现在configuration中,properties中的信息几乎为空,这也就印证了刚才的推测。

5733d52c132dcb6c96a5bfc65725336c.jpeg

结合上图与configuration的代码来看,该configuration对象还是任务重试之前的对象(排除new一个新的没有加载配置文件的情况),且至少调用了一次reloadConfiguration,因为在该函数中会将properties置为NULL,在重新调用get时,触发懒加载,新建一个properties实例对象。

但是,有疑惑的地方是:懒加载时,不管怎样,都会将overlay中的内容重新拷贝到properties中,而实际情况却没有。

7595ca33fbe905d414bf7b025346b475.jpeg

所以,问题变成为什么configuration中的properties会清空,并且没有将overlay中的数据拷贝到properties中。这个问题和访问已经关闭的classLoader抛出异常有什么关联?

再次复现问题进行分析,这次发现出现该问题时,configuration中的classLoader(实际上是flink中的SafetyNetWrapperClassLoader)中的inner为NULL。

之所以为空是因为任务失败时,最终会调用classLoader的close方法。对于配置参数"classLoader.check-leaked-classLoader"为true时,会使用一个包装类,该包装类的close方法就是将真正的classLoader置为NULL。

而在加载资源的时候会调用classLoader中的inner去获取资源,inner为NULL导致直接抛异常出去,指导最外层被捕获。这样在configuration中也就不会执行将overlay中的信息拷贝到properties的逻辑了。

19858f6990f9ad33e9f0e35040571907.jpeg

至此,问题的原因已经基本清楚:就是因为任务失败,classLoader被置为空,在加载资源时直接抛异常,引起无法正确获取的服务的key并成功构建principal,导致任务失败。

那么,classLoader什么时候会被置为NULL,以及configuration又是什么时候触发调用的reloadConfiguration导致properties被清空的呢?

在刚才的问题复现过程中,其实发现了这么一个堆栈信息:

fdf6c7d593d018b1a3ddc31de53830aa.jpeg

结合对应的代码来分析,发现构造YarnConfiguration时,触发了类加载的静态方法调用,以至于调用了reloadConfiguration,将properties清空。

35e4c8785608631c60bf7bb64f7834dd.jpeg

【问题解决】

问题原因都已经分析清楚后,接下来自然而然就是如何解决或规避问题。

一种简单的处理方式是:在flink中,将配置项"classLoader.check-leaked-classLoader"置为false,这样就不会使用到包装类SafetyNetWrapperClassLoader。虽然任务失败时还是会调用classLoader的方法,但在任务重试时,在缓存文件系统对象中的conf的cloassLoader不会被清空,并且还可以继续使用,自然而然也就不会出现问题。

另外,在分析过程中发现,客户端使用的HDFS文件系统句柄是有缓存的,具体以资源名、资源的认证方式以及当前用户名构成唯一key。缓存是没有大小限制的,在没有对句柄主动调用close方法时,都会一直存在缓存中。

因此,如果业务进程是短时间使用一般不会有太大问题。而如果是常驻服务,并且是提供代理功能,那么就可能因为缓存,而出现内存泄露问题。

当然,可以通过将配置项"fs.hdfs.impl.disable.cache"设置为true,来禁用缓存。

【问题引申】

该问题为什么在配套之前的hadoop版本(2.8.5)没有出现,而到了2.10.1之后就出现了?

仔细对比了两个版本相关代码之间的区别后,发现两个版本的Master类的实现确实有所不同。老的版本不会触发加载YarnConfiguration类。因此也就不会有问题。老版本的代码如下所示(新版本的可以见前面的图)

public static String getMasterPrincipal(Configuration conf) 
    throws IOException {
    String masterHostname = getMasterAddress(conf).getHostName();
    // get kerberos principal for use as delegation token renewer
    return SecurityUtil.getServerPrincipal(getMasterUserName(conf), masterHostname);
}

【总结】

本文对flink任务失败重试,classLoader关闭后引发的问题进行了分析定位,同时也简单梳理了涉及到的hadoop配置类、文件系统句柄缓存、以及flink自身涉及的参数等内容。

好了,这就是本文的全部内容,如果觉得本文对您有帮助,请点赞+转发,也欢迎加我微信交流~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值