YarnClient的init方法执行后无反应
比较玄学,在使用YarnClient来查询Spark任务ID的时候,初始化方法执行完没有任何反应,相同的代码在自己Windows电脑上跑的好好的,找了一个Linux的机器也是可以的,但是放到容器里面就不行(这是什么鬼)。最终发现,原来成败仅在一念之间。。。
### 1. 主要功能
SpringBoot启动后,连接zookeeper进行选主,抢主成功的节点连接Yarn进行任务查询,如果存在RUNNING的任务,将其kill掉,并重新提交新的Spark任务。(为的是在升级或重新安装的场景,Springboot的生命周期能和yarn上面spark任务的生命周期保持“一致”)。
### 2. 上代码
```
@Slf4j
public class AppYarnClient {
private final YarnClient client;
public AppYarnClient() {
client = YarnClient.createYarnClient();
Configuration conf = new Configuration();
conf.addResource(new Path("/opt/huawei/iss/conf/core-site.xml"), false);
conf.addResource(new Path("/opt/huawei/iss/conf/yarn-site,xml"), false);
client.init(conf);
log.info("yarn client init finished...");
client.start();
log.info("yarn client start finished...");
}
public List getRunningAppIds(String appName) {
List applications = null;
try {
EnumSet appStates = EnumSet.noneOf(YarnApplicationState.class);
appStates.add(YarnApplicationState.RUNNING);
applications = client.getApplications(appStates);
} catch (YarnException | IOException ex) {
log.error("get yarn applications failed. ", ex);
}
List runningApps = new ArrayList();
if (CollectionUtils.isEmpty(applications)) {
return runningApps;
}
runningApps = applications.stream()
.filter(report -> report.getName().contains(appName))
.map(ApplicationReport::getApplicationId)
.collect(Collectors.toList());
return runningApps;
}
public boolean killApplication(ApplicationId appId) {
try {
client.killApplication(appId);
} catch (YarnException | IOException ex) {
log.error("yarn kill application of {} failed, because: ", appId.toString(), ex);
return false;
}
return true;
}
public void killAndSubmit() {
log.info("the yarn client will work...");
ShellUtils shellUtils = new ShellUtils();
List runningAppIds = getRunningAppIds("com.huawei.iss.ce.statistic.streaming.StatisticEngine");
log.info("the running apps are {}", runningAppIds);
if (CollectionUtils.isEmpty(runningAppIds)) {
log.info("there are no running app!");
shellUtils.execute(new String[] {"sh", "-x", "/opt/huawei/iss/components/ComputingEngine/setup/start.sh"});
return;
}
for (ApplicationId runningAppId : runningAppIds) {
boolean killResult = killApplication(runningAppId);
log.info("kill app result is: {}, the appId is {}", killResult, runningAppId.toString());
}
log.info("exist apps have been killed, will submit again.");
shellUtils.execute(new String[] {"sh", "-x", "/opt/huawei/iss/components/ComputingEngine/setup/start.sh"});
}
}
```
### 3. 问题描述
在执行完yarnClient.init(conf)后,日志只打印了一句:Service: org.apache.hadoop.yarn.client.api.impl.YarnClientImpl entered state INITED,然后这个线程就结束了,没有后续,debug日志开启也没有异常打印(黑人问号???)
![image.png](https://s2.51cto.com/images/20210705/1625488530602652.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
我就去查看源码,发现这一句是在状态转换的时候打印的:
![image.png](https://s2.51cto.com/images/20210705/1625488587297828.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
但是里面除了属性赋值,没有其他的操作,不会有异常抛出(继续满脸疑惑???)。这时候我继续看源码,发现会有一个创建连接的过程,入口是在创建YarnClientImpl的时候:
```
@Public
public static YarnClient createYarnClient() {
YarnClient client = new YarnClientImpl();
return client;
}
// 然后在serviceInit方法里面,这方法好像在哪见过? 哦!就是在yarnClient.init(conf)里面
protected void serviceInit(Configuration conf) throws Exception {
......
if (YarnConfiguration.timelineServiceV1Enabled(conf)) {
this.timelineV1ServiceEnabled = true;
this.timelineDTRenewer = getTimelineDelegationTokenRenewer(conf);
this.timelineService = TimelineUtils.buildTimelineTokenService(conf);
}
if (this.timelineV1ServiceEnabled || conf.getBoolean("yarn.timeline-service.generic-application-history.enabled", false)) {
this.historyServiceEnabled = true;
this.historyClient = AHSClient.createAHSClient();
this.historyClient.init(conf);
}
......
super.serviceInit(conf);
}
```
正常执行的情况,AHSClient.createAHSClient();这一句是可以正常初始化并连接到对应yarn节点的,但是在容器里没有打印它的执行日志。
怀疑点1:容器网络配置有问题???
检查端口和主机映射,完全没毛病,况且我zookeeper都用得好好的呢(傲娇)—— 排除此怀疑。
怀疑点2:用的配置文件有问题?(core-site.xml和yarn-site.xml)
将容器里的配置文件拿出来对比客户端用到的配置文件,完全一样!!! —— 排除此怀疑。
怀疑点3:kerberos认证失败???
检查kerberos认证过程,漏掉一个文件,补上,问题还是没解决。—— 排除此怀疑。
开始怀疑人生。。。是不是错误日志被屏蔽了,没有输出来???
尝试把错误日志输出到执行脚本的控制台,然后 ClassNotFind: javax.ws.rs.ext.MessageBodyReader,在代码里搜索了一下:
![image.png](https://s2.51cto.com/images/20210705/1625489790511832.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
这好像也莫得问题呀。。。查看打出来的jar包,emmm,少了一个jar。
依赖树打出来看一下:都是provide,难怪没有,加到容器里去,然后重启服务,牛批!!!好了。
### 总结
刚接触大数据组件一周,感受就是:咋动不动就是jar包冲突,类找不到呢!!!
网上可以能搜到很多大同小异的问题和解决办法(网上能搜到的解决办法我也试过,对我来说没有帮我解决问题,所以没有列举),但是如果某一天遇到了奇奇怪怪的问题并且没有解决办法时,尝试怀疑一下自己引入的jar包是否是正常的(冲突,版本不对,少引入)。另外很多源码里的日志被屏蔽了,尝试把日志输出到控制台,看看究竟报的是什么错,有助于解决问题!!!