hadoop2.7之作业提交详解(下)
接着作业提交详解(上)继续写:在上一篇(hadoop2.7之作业提交详解(上))中已经讲到了YARNRunner.submitJob()
[WordCount.main() -> Job.waitForCompletion() -> Job.submit() -> Job.connect() -> Cluster.Cluster() -> Cluster.initialize() -> YarnClientProtocolProvider.create() -> JobSubmitter.sbumitJobInternal() -> YARNRunner.submitJob()]
那么现在接着从YARNRunner.submitJob()开始说:
先简单看一下YARNRunner这个类(摘录一部分):
package org.apache.hadoop.mapred;
public class YARNRunner implements ClientProtocol {
private ResourceMgrDelegate resMgrDelegate; //这是RM派驻在“地方”上的特派员
private ClientCache clientCache;
private Configuration conf;
private final FileContext defaultFileContext; public YARNRunner(Configuration conf) {//构造函数,需要创建特派员,然后调用下一个构造函数
this(conf, new ResourceMgrDelegate(new YarnConfiguration(conf)));
} public YARNRunner(Configuration conf, ResourceMgrDelegate resMgrDelegate) {//需要创建ClientCache
this(conf, resMgrDelegate, new ClientCache(conf, resMgrDelegate));
} public YARNRunner(Configuration conf, ResourceMgrDelegate resMgrDelegate,
ClientCache clientCache) {//这是最终的构造函数
this.conf = conf;
try {
this.resMgrDelegate = resMgrDelegate;
this.clientCache = clientCache;
this.defaultFileContext = FileContext.getFileContext(this.conf);
} catch (UnsupportedFileSystemException ufe) {
throw new RuntimeException("Error in instantiating YarnClient", ufe);
}
}
public JobStatus submitJob(JobID jobId, String jobSubmitDir, Credentials ts)
throws IOException, InterruptedException { addHistoryToken(ts);//用于为历史记录服务,与“作业历史(JobHistory)”有关 // Construct necessary information to start the MR AM
//构建MR AM的必要启动信息
//创建一个ApplicationSubmissionContext,并将conf中的相关信息转移过去
ApplicationSubmissionContext appContext =
createApplicationSubmissionContext(conf, jobSubmitDir, ts); // Submit to ResourceManager
try {
/* 将作业提交给资源管理者(ResourceManager)*/
//RM受理了所提交的作业以后,会把这个ContainerLaunchContext转发到某个NM节点
//上,在那里执行这个shell命令行,另起一个Java虚拟机,让它执行MRAppMaster.class。
//由此可见,这个ApplicationSubmissionContext对象appContext,真的是“代表着ResourceManager
//为发起该应用的ApplicationMaster所需的全部信息”
ApplicationId applicationId =
resMgrDelegate.submitApplication(appContext); ApplicationReport appMaster = resMgrDelegate
.getApplicationReport(applicationId);
String diagnostics =
(appMaster == null ?
"application report is null" : appMaster.getDiagnostics());
if (appMaster == null
|| appMaster.getYarnApplicationState() == YarnApplicationState.FAILED
|| appMaster.getYarnApplicationState() == YarnApplicationState.KILLED) {
throw new IOException("Failed to run job : " +
diagnostics);
}
return clientCache.getClient(jobId).getJobStatus(jobId);
} catch (YarnException e) {
throw new IOException(e);
}
}
}
其中createApplicationSubmissionContext方法的作用:
1、设置资源:默认内存为1536M,cpu的core为1
2、设置本地资源,比如临时工作目录,jar包等
3、设置安全票据tokens
4、设置启动AM的命令
5、检查map和reduce的配置信息
6、设置环境CLASSPATH等
7、为AM的container设置ContainerLaunchContext
8、设置ApplicationSubmissionContext
9、设置MRAppMaster的执行路径
并把配置块conf中当前的相关信息、已上传资料所在的目录路径以及有关身份和访问权限的信息都复制转移过去。提供了有关ApplicationMaster即“项目组长”该用哪一个Shell(例如bash)以及有关某些环境变量的信息。再如作业的名称等。
接下来就是调用ResourceMgrDelegate.submitApplication方法:(所以我们先看一下ResourceMgrDelegate这个类)
public class ResourceMgrDelegate extends YarnClient {
private YarnConfiguration conf;
private ApplicationSubmissionContext application;
private ApplicationId applicationId;
protected YarnClient client;//实际上是YarnClientImpl类的对象,那也是对YarnClient的继承和扩展
private Text rmDTService;
//这是ResourceMgrDelegate的构造方法
public ResourceMgrDelegate(YarnConfiguration conf) {
super(ResourceMgrDelegate.class.getName());
this.conf = conf;
//创建YarnClient对象client
//YarnClient.createYarnClient()创建的是YarnClientImpl
this.client = YarnClient.createYarnClient();
init(conf);//这是由AbstractService类提供的,YarnClient是对AbstractService的扩展
start();//这也是由AbstractService类提供的
}
public ApplicationId
submitApplication(ApplicationSubmissionContext appContext)
throws YarnException, IOException {
return client.submitApplication(appContext);//调用YarnClientImpl.submitApplication方法
}
从前面所有的代码中我们可以得知:
ResourceMgrDelegate对象是在YARNRunner的构造函数中创建的。而YARNRunner,则是在前面的Cluster.Initialize()中创建的。再往上追溯,则Cluster类对象是在首次调用connect()时创建的。所以,任何一个节点,只要曾经调用过connect(),即曾经与“集群”有过连接,节点上就会有个Cluster类对象,从而就会有个YARNRunner对象,也就会有个ResourceMgrDelegate对象,而且如下所述就会有个YarnClientImpl对象。
现在为止,我们的作业提交路径是:
[WordCount.main() -> Job.waitForCompletion() -> Job.submit() -> Job.connect() -> Cluster.Cluster() -> Cluster.initialize() -> YarnClientProtocolProvider.create() -> JobSubmitter.sbumitJobInternal() -> YARNRunner.submitJob() -> ResourceMgrDelegate.submitApplication() -> YarnClientImpl.submitApplication()]
解下来我们继续看YarnClientImpl.submitApplication()方法:
public ApplicationId
submitApplication(ApplicationSubmissionContext appContext)
throws YarnException, IOException {
ApplicationId applicationId = appContext.getApplicationId();
if (applicationId == null) {
throw new ApplicationIdNotProvidedException(
"ApplicationId is not provided in ApplicationSubmissionContext");
}
//创建一个SubmitApplicationRequestPBImpl类的记录块
SubmitApplicationRequest request =
Records.newRecord(SubmitApplicationRequest.class);
request.setApplicationSubmissionContext(appContext);//设置好记录块中的Context
// Automatically add the timeline DT into the CLC
// Only when the security and the timeline service are both enabled
if (isSecurityEnabled() && timelineServiceEnabled) {
addTimelineDelegationToken(appContext.getAMContainerSpec());
} //TODO: YARN-1763:Handle RM failovers during the submitApplication call.
rmClient.submitApplication(request);//实际的跨节点提交 int pollCount = 0;
long startTime = System.currentTimeMillis();
EnumSet<YarnApplicationState> waitingStates =
EnumSet.of(YarnApplicationState.NEW,
YarnApplicationState.NEW_SAVING,
YarnApplicationState.SUBMITTED);
EnumSet<YarnApplicationState> failToSubmitStates =
EnumSet.of(YarnApplicationState.FAILED,
YarnApplicationState.KILLED);
while (true) {
try {
//获取来自RM节点的应用状态报告,从中获取本应用的当前状态
ApplicationReport appReport = getApplicationReport(applicationId);
YarnApplicationState state = appReport.getYarnApplicationState();
if (!waitingStates.contains(state)) {
if(failToSubmitStates.contains(state)) {
throw new YarnException("Failed to submit " + applicationId +
" to YARN : " + appReport.getDiagnostics());
}
LOG.info("Submitted application " + applicationId);
break;//作业已进入运行阶段,结束while循环
} long elapsedMillis = System.currentTimeMillis() - startTime;
if (enforceAsyncAPITimeout() &&
elapsedMillis >= asyncApiPollTimeoutMillis) {
throw new YarnException("Timed out while waiting for application " +
applicationId + " to be submitted successfully");
} // Notify the client through the log every 10 poll, in case the client
// is blocked here too long.
if (++pollCount % 10 == 0) {
LOG.info("Application submission is not finished, " +
"submitted application " + applicationId +
" is still in " + state);
}
try {
Thread.sleep(submitPollIntervalMillis);
} catch (InterruptedException ie) {
String msg = "Interrupted while waiting for application "
+ applicationId + " to be successfully submitted.";
LOG.error(msg);
throw new YarnException(msg, ie);
}
} catch (ApplicationNotFoundException ex) {
// FailOver or RM restart happens before RMStateStore saves
// ApplicationState
LOG.info("Re-submit application " + applicationId + "with the " +
"same ApplicationSubmissionContext");
rmClient.submitApplication(request);//失败后的再次提交
}
} return applicationId;
}
从上看来只要是调用了rmClient.submitApplication(request)方法,那这儿rmClient又是个什么呢?我们接着来看一下YarnClientImpl这个类的简单定义:
public class YarnClientImpl extends YarnClient { private static final Log LOG = LogFactory.getLog(YarnClientImpl.class); protected ApplicationClientProtocol rmClient;
protected long submitPollIntervalMillis;
private long asyncApiPollIntervalMillis;
private long asyncApiPollTimeoutMillis;
protected AHSClient historyClient;
private boolean historyServiceEnabled;
protected TimelineClient timelineClient;
@VisibleForTesting
Text timelineService;
@VisibleForTesting
String timelineDTRenewer;
protected boolean timelineServiceEnabled;
protected boolean timelineServiceBestEffort; private static final String ROOT = "root"; public YarnClientImpl() {
super(YarnClientImpl.class.getName());
}
从上可以看出rmClient是一个ApplicationClientProtocol对象,这个又是一个接口,具体的实现类是ApplicationClientProtocolPBClientImpl ,接下来我们看一下这个类:
public class ApplicationClientProtocolPBClientImpl implements ApplicationClientProtocol,
Closeable { private ApplicationClientProtocolPB proxy; public ApplicationClientProtocolPBClientImpl(long clientVersion,
InetSocketAddress addr, Configuration conf) throws IOException {
//将配置项“rpc.engine.ApplicationClientProtocolPB”设置成ProtobufRpcEngine
RPC.setProtocolEngine(conf, ApplicationClientProtocolPB.class,
ProtobufRpcEngine.class);
//创建proxy
//这个proxy存在于用户为提交运行具体应用而起的那个JVM上,它既不属于
//ResourceManager,也不属于NodeManager,而是一个独立的Java虚拟机,可以是在集群//内的任何一台机器上
proxy = RPC.getProxy(ApplicationClientProtocolPB.class, clientVersion, addr, conf);
} public SubmitApplicationResponse submitApplication(
SubmitApplicationRequest request) throws YarnException,
IOException {
//从请求request中取出其协议报文(message)部分
SubmitApplicationRequestProto requestProto =
((SubmitApplicationRequestPBImpl) request).getProto();
try {
//交由proxy将报文发送出去,并等候服务端回应
//将服务端回应包装成SubmitApplicationResponsePBImpl对象
return new SubmitApplicationResponsePBImpl(proxy.submitApplication(null, requestProto));
} catch (ServiceException e) {
RPCUtil.unwrapAndThrowException(e);
return null;
}
}
}
ApplicationClientProtocolPBClientImpl的submitApplication方法,在其里面就是调用proxy.submitApplication方法,而proxy是在构造函数中创建的。
通过proxy发出的SubmitApplicationRequest,是以RM节点为目标的,最终经由操作系统提供的网络传输层以TCP报文的方式送达RM所在节点机上的对等层,那上面是
ProtoBuf,它会从TCP报文中还原出对端所发送的对象。再往上,那就是同样也实现了ApplicationClientProtocolPB界面的ApplicationClientProtocolPBServiceImpl,ProtoBuf这一
层会根据对方请求直接就调用其submitApplication()。这样,Client一侧对于ApplicationClientProtocolPBClientImpl所提供函数的调用就转化成Server一侧对于applicationClientProtocolPBServiceImpl所提供的对应函数的调用。当然,Server一侧函数调用的返回值也会转化成Client一侧的返回值,这就实现了远程过程调用RPC。不言而喻,Client/Server双方的这两个对象必须提供对同一个界面的实现,在这里就是ApplicationClientProtocolPB。
Client端
YARNRunner.submitJob() //这是处于顶层的应用层
ResourceMgrDelegate.submitApplication() //这是RM的代理
YarnClientImpl.submitApplication() //YARN框架的Client一侧
ApplicationClientProtocolPBClientImpl.submitApplication()//ApplicationClientProtocol界面
proxy.submitApplication() //ApplicationClientProtocolPB界面
Protocol内部实现的submitApplication() //在TCP/IP的基础上发送应用层的请求
Socket和TCP/IP //这是网络连接的最低层
Server端:
Server这一边就不同了。在Server这一边,结构的层次和函数调用的层次是相反的,结构上处于最底层的Socket和TCP/IP反倒处于函数调用栈的最高层,愈往下调用实质上就愈往结构上的高层走。这是因为TCP/IP报文最初到达的是底层,然后逐层往上递交的过程一般都是通过函数调用实现的,所以层层往下调用的过程反倒变成了层层往上递交的过程。
那么接下来就是通过tcp/ip调用服务端ApplicationClientProtocolPBServiceImpl.submitApplication()方法;
public class ApplicationClientProtocolPBServiceImpl implements ApplicationClientProtocolPB { private ApplicationClientProtocol real; public ApplicationClientProtocolPBServiceImpl(ApplicationClientProtocol impl) {
this.real = impl;
}
public SubmitApplicationResponseProto submitApplication(RpcController arg0,
SubmitApplicationRequestProto proto) throws ServiceException {
SubmitApplicationRequestPBImpl request = new SubmitApplicationRequestPBImpl(proto);//创建一个请求
try {
SubmitApplicationResponse response = real.submitApplication(request);
//real为ClientRMService类对象 ,该对象在RM初始化时由createClientRMService() 方法创建
return ((SubmitApplicationResponsePBImpl)response).getProto();
} catch (YarnException e) {
throw new ServiceException(e);
} catch (IOException e) {
throw new ServiceException(e);
}
}
}
接下来调用ClientRMService.submitApplication(request); 方法
public SubmitApplicationResponse submitApplication(
SubmitApplicationRequest request) throws YarnException {
ApplicationSubmissionContext submissionContext = request
.getApplicationSubmissionContext();
ApplicationId applicationId = submissionContext.getApplicationId(); // ApplicationSubmissionContext needs to be validated for safety - only
// those fields that are independent of the RM's configuration will be
// checked here, those that are dependent on RM configuration are validated
// in RMAppManager. String user = null;
try {
// Safety
user = UserGroupInformation.getCurrentUser().getShortUserName();//获取用户
} catch (IOException ie) {
LOG.warn("Unable to get the current user.", ie);
RMAuditLogger.logFailure(user, AuditConstants.SUBMIT_APP_REQUEST,
ie.getMessage(), "ClientRMService",
"Exception in submitting application", applicationId);
throw RPCUtil.getRemoteException(ie);
} // Check whether app has already been put into rmContext,
// If it is, simply return the response
//判断作业是否已经存在,如果是则直接返回实例
if (rmContext.getRMApps().get(applicationId) != null) {
LOG.info("This is an earlier submitted application: " + applicationId);
return SubmitApplicationResponse.newInstance();
}
//如果没有设置队列,则使用默认队列
if (submissionContext.getQueue() == null) {
submissionContext.setQueue(YarnConfiguration.DEFAULT_QUEUE_NAME);
}
//如果没有设置application名字,则使用默认的命名规则
if (submissionContext.getApplicationName() == null) {
submissionContext.setApplicationName(
YarnConfiguration.DEFAULT_APPLICATION_NAME);
}
//如果没有指定提交类型,则指定默认为yarn模式
if (submissionContext.getApplicationType() == null) {
submissionContext
.setApplicationType(YarnConfiguration.DEFAULT_APPLICATION_TYPE);
} else {
if (submissionContext.getApplicationType().length() > YarnConfiguration.APPLICATION_TYPE_LENGTH) {
submissionContext.setApplicationType(submissionContext
.getApplicationType().substring(0,
YarnConfiguration.APPLICATION_TYPE_LENGTH));
}
} try {
// call RMAppManager to submit application directly
rmAppManager.submitApplication(submissionContext,
System.currentTimeMillis(), user);//提交作业到rmAppManager手中 LOG.info("Application with id " + applicationId.getId() +
" submitted by user " + user);
RMAuditLogger.logSuccess(user, AuditConstants.SUBMIT_APP_REQUEST,
"ClientRMService", applicationId);
} catch (YarnException e) {
LOG.info("Exception in submitting application with id " +
applicationId.getId(), e);
RMAuditLogger.logFailure(user, AuditConstants.SUBMIT_APP_REQUEST,
e.getMessage(), "ClientRMService",
"Exception in submitting application", applicationId);
throw e;
} SubmitApplicationResponse response = recordFactory
.newRecordInstance(SubmitApplicationResponse.class);
return response;
}
从作业提交的角度看,一旦进入了 RM 节点上的RMAppManagers. ubmitApplication(),作业的提交就已完成。 至于这以后的处理,那是 RM的事了,作业提交的最终流程就是:
[WordCount.main() -> Job.waitForCompletion() -> Job.submit() -> Job.connect() -> Cluster.Cluster() -> Cluster.initialize() -> YarnClientProtocolProvider.create() -> JobSubmitter.sbumitJobInternal() -> YARNRunner.submitJob() -> ResourceMgrDelegate.submitApplication() -> YarnClientImpl.submitApplication() -> ApplicationClientProtocolPBClientImpl.submitApplication() -> ApplicationClientProtocolPBServiceImpl.submitApplication() -> ClientRMService.submitApplication() -> RMAppManager.submitApplication() ]
hadoop2.7之作业提交详解(下)的更多相关文章
- hadoop2.7之作业提交详解(上)
根据wordcount进行分析: import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; impo ...
- hadoop2.7作业提交详解之文件分片
在前面一篇文章中(hadoop2.7之作业提交详解(上))中涉及到文件的分片. JobSubmitter.submitJobInternal方法中调用了int maps = writeSplits(j ...
- [js高手之路]深入浅出webpack教程系列3-配置文件webpack.config.js详解(下)
本文继续接着上文,继续写下webpack.config.js的其他配置用法. 一.把两个文件打包成一个,entry怎么配置? 在上文中的webpack.dev.config.js中,用数组配置entr ...
- SSL/TLS协议详解(下)——TLS握手协议
本文转载自SSL/TLS协议详解(下)--TLS握手协议 导语 在博客系列的第2部分中,对证书颁发机构进行了深入的讨论.在这篇文章中,将会探索整个SSL/TLS握手过程,在此之前,先简述下最后这块内容 ...
- .Net Attribute详解(下) - 使用Attribute武装枚举类型
接上文.Net Attribute详解(上)-Attribute本质以及一个简单示例,这篇文章介绍一个非常实用的例子,相信你一定能够用到你正在开发的项目中.枚举类型被常常用到项目中,如果要使用枚举To ...
- IE8"开发人员工具"使用详解下(浏览器模式、文本模式、JavaScript调试、探查器)
来源: http://www.cnblogs.com/JustinYoung/archive/2009/04/03/kaifarenyuangongju2.html 在上一篇文章IE8“开发人员工具” ...
- CSS2.1SPEC:视觉格式化模型之width属性详解(下)
本文承接CSS2.1SPEC:视觉格式化模型之width属性详解(上),继续分析CSS视觉格式化模型中width以及相关值的计算问题: 注:与上节不同,本节的demo中由于出现了float,absol ...
- Linux常用命令详解下
Linux常用命令详解 目录 一.Linux常用命令 1.1.查看及切换目录(pwd.cd.ls.du) 1.2.创建目录和文件(mkdir.touch.ln) 1.3.复制.删除.移动目录和文件(c ...
- Hadoop2.x Yarn作业提交(客户端)
转自:http://blog.csdn.net/lihm0_1/article/details/22186833 YARN作业提交的客户端仍然使用RunJar类,和MR1一样,可参考 http://b ...
随机推荐
- 判断小端大端(C实现)
C程序中的大端小端概念可以参见上一篇文章 如何区分小端和大端.本文重点讨论如何用C程序来判断当前系统是大端还是小端. 判断大端小端的程序如下: #include <stdio.h> int ...
- python实现DFA模拟程序(附java实现代码)
DFA(确定的有穷自动机) 一个确定的有穷自动机M是一个五元组: M=(K,∑,f,S,Z) K是一个有穷集,它的每个元素称为一个状态. ∑是一个有穷字母表,它的每一个元素称为一个输入符号,所以也陈∑ ...
- MyBatis 接口多参数的处理方法
From<MyBatis从入门到精通> 1.接口类中增加的方法: /* 2.7 多个接口参数的用法 多个参数时,可以选取的方案有:使用Map类型或者使用@Param注解 使用Map类型作为 ...
- Mllib数据类型(密集向量和稀疏向量)
1.局部向量 Mllib支持2种局部向量类型:密集向量(dense)和稀疏向量(sparse). 密集向量由double类型的数组支持,而稀疏向量则由两个平行数组支持. example: 向量(5.2 ...
- 5G VS WiFi6,实力大比拼!
移动通信与WiFi,就像移动设备上的两大高手.彼此势均力敌:一个主室内,一个主室外.WiFi是移动网络的室内覆盖补充,也承担着大量的数据流量卸载,二者既想各自占山为王,但也时不时地相互成全对方. 然而 ...
- weex起步
weex文档地址: http://weex-project.io/cn/guide/index.html weex的文档过于简单,加上js语法 & android & ios本身也有很 ...
- linux 使用git
linux下使用git管理代码 环境:centos7.2 : 工具:xshell 1.安装git,命令如下: #yum install git 2.设置用户名和邮箱,命令如下: #git c ...
- PHP与ECMAScript_2_数据类型
PHP ECMAScript 数据类型 基本:String.Integer.Float.Boolean 基本:String. Number. Boolean.NULL.undefined 复合:Arr ...
- TestNG中DataProvider的用法二:简单的数据驱动
@DataProvider标记的方法除了可以返回数组外,还可以返回一个Iterator,这样的好处是不用把所有的测试数据都加载到内存中,而是需要的时候就读一条. 下面的例子就使用了Iterator,然 ...
- mybatis动态插入数据库
<insert id="dynamicAddUser"> insert into t_user <!-- trim 对所有的表中列名 进行动态处理 --> ...