前言
在使用hadoop集群的时候,所有的任务都是最终以Application的形式跑在集群中,不管你是自己写的MR程序亦或是你写的hive sql转化成的mr任务,最终都是以Application应用的身份在跑.这些Application跑完之后,这些信息在jobHistory中就可以看了,可以说hadoop在这方面做得真的非常完整.但是完善归完善.但是jobHistory可以说是一种"事后分析",而有的时候我们反而是需要去控制目前所在的Applications.举个例子,比如某用户误启动了几十个应用,这些应用又是非常消耗资源的,一般这时候集群的管理者就会想尽办法去kill这些应用,否则资源都被占了,别人怎么使用.但是比较不幸的一点,目前Yarn在kill应用的操作上还没有提供批处理的方式,只有最简单的kill+appID的形式,都是单一的操作,所以会造成最后"应用是kill掉了,但是花了半个小时时间."这样的结果.所以本文就来分析一下批处理kill应用的解决方案,从应用场景到方案设计再到最终实现,依次进行描述.
应用场景
1.误操作启动大量应用.就是文章开头所描述到的场景.这个在小集群,少用户的情况下不容易发生,但是一旦集群具备了一定规模,达到多组用户的时候,这种现象发生的概率就会比较高了.用户多了,遇到这种问题,必须得解决吧,而且必须得高效解决.
2.第二种情况发生在Application recovery阶段,比如说当你的集群中开启了yarn的recovery功能之后,此时做ResourceManager重启的时候,他会从rmStateStore中读取上次保存下来的Application信息,然后进行全部恢复。这个时候如果你不想要恢复上次由于RM服务停止造成中断的应用,那么你也需要有个方法去批量kill正在此时恢复的应用。这种场景就不是被动发生的,在某些场景下他就会出现,发生。
所以总结上面提到的2个case,1个是偏外部因素,1个是偏内部因素,但是都说明了我们需要1个方法能够去批量执行kill应用的操作。
现有的Kill Application命令
现有存在于YARN中的kill应用的操作命令很简单,如下
bin/yarn application -kill <applicationId>
没用过这个命令的同学可以在自己的测试集群中输入这个命令玩一下。同时yarn application下还有一些其他的命令参数,输入下面这个命令可以打出帮助信息
bin/yarn application
批处理Kill Applications CLI设计
CLI是CommandLine的简称,就是命令行,上一段中的-kill <applicationId>在Yarn是是被定义为1个ApplicationCLI,是针对Application的命令操作.表现出来的形式是以YarnCli的形式被调用,所以会在前面看到yarn这个前缀.所以我们的1个目标就是创造出一些能进行批处理的执行kill应用的操作命令.但是这里会冒出另外1个问题,如果进行批量执行应用操作,那么这些待kill的应用一定具有某种类似或相同的属性,否则我们就无法划分,归类了.首先需要找出在Yarn中的Application到底有哪些属性,我截取出了部分代码:
@Private
public class Application {
private static final Log LOG = LogFactory.getLog(Application.class);
private AtomicInteger taskCounter = new AtomicInteger(0);
private AtomicInteger numAttempts = new AtomicInteger(0);
final private String user;
final private String queue;
final private ApplicationId applicationId;
...
另外一个project中的定义:
public interface Application extends EventHandler<ApplicationEvent> {
String getUser();
Map<ContainerId, Container> getContainers();
ApplicationId getAppId();
ApplicationState getApplicationState();
}
所以综合以上这2个类的定义,可以跳出3个比较常见的属性作为CLI参数,
第一.user,用户名.
第二.queue,所属队列.
第三.applicationState.应用状态,比如我想kill正在running状态的应用或是处于Accepted但是还没开始running的应用,这也是1个不错的指标维度.
我们最终想要达到的效果是这样的:
-killByAppStates <States> The states of application that will be killed.
-killByUser <User name> Kill running-state applications of specific user.
-killOfQueue <Queue Name> Kill the applications of specific queue.
这里需要提出一点建议,对于第二个命令-killByUser,因为涉及到直接关联用户的操作命令,如果操作者是普通用户,不一定会有权限去kill别人的应用,所以这个命令更应该放在RmAdminCLI的命令中而不是YarnCLI.这一点在后面的具体代码实现中会加以区分.
ResourceManager的CLI相关服务
在打算具体实现新的命令接口,需要首先对rm对应的内部对应服务有1个了解.ResourceManager在这里的设计可以说是比较巧妙的,为了避免客户端命令的服务影响admin超级管理员的命令服务,他将这2套服务进行了分开处理,即AdminService和ClientRMService,然后被RMAdminCLI和ApplicationCLI所远程调用,具体的结构如下
所以在后面需要改动代码的地方就是在图中出现的几个主要类中.
批处理Kill Application操作实现
对Yarn新增命令行操作,难的并不是他的实现,而是在于他的流程,因为有些代码是自己生成的,你需要改一些.proto的文件,下面演示一下如何改ApplicationCLI,RMAdminCLI将简单描述.
第一步.定义rpc方法接口以及请求回复类
import "Security.proto";
import "yarn_service_protos.proto";
service ApplicationClientProtocolService {
....
rpc killApplicationsByAppStates (KillApplicationsByAppStatesRequestProto) returns (KillApplicationsByAppStatesResponseProto);
rpc killApplicationsOfQueue (KillApplicationsOfQueueRequestProto) returns (KillApplicationsOfQueueResponseProto);
}
...
message KillApplicationsByAppStatesRequestProto {
repeated YarnApplicationStateProto application_states = 1;
}
message KillApplicationsByAppStatesResponseProto {
optional bool is_kill_completed = 1 [default = false];
}
message KillApplicationsOfQueueRequestProto {
required string queue = 1;
}
message KillApplicationsOfQueueResponseProto {
optional bool is_kill_completed = 1 [default = false];
}
里面不明白的地方,请自行查阅protocolbuffer的使用说明.最后使用编译命令重新打包,比如"mvn package -Dmaven.test.skip=true",以此生成相应代码.
第二步.实现Request,Response类以及具体的Impl实现类
比如以KillByAppStates的命令为例子:
/**
* Licens