JobTracker源码分析
前言
JobTracker是Hadoop中的一个重要角色,负责任务的调度和分配,和client端的任务提交也有关系,这次主要分析JobTracker中JobTracker和TaskTracker心跳机制在JobTracker这端的详细实现过程以及client提交的任务是如何被处理然后分配给TaskTracker的。
JobTracker启动
在hadoop-1.1.2的src/mapred里面的org.apache.hadoop.mapred里面可以找到JobTracker.java,说到启动,JobTracker是以一个单独的进程运行的,因此可以发现JobTracker.java里面是有主函数的,进入到main()函数
public static void main(String argv[]
) throws IOException, InterruptedException {
//打印一条启动的日志
StringUtils.startupShutdownMessage(JobTracker.class, argv, LOG);
try {
if(argv.length == 0) {
JobTracker tracker = startTracker(new JobConf()); //核心的启动函数,在里面实例了几个重要对象
tracker.offerService(); //将某些重要对象启动,作为rpcserver或者httpserver
}
else {
if ("-dumpConfiguration".equals(argv[0]) && argv.length == 1) {
dumpConfiguration(new PrintWriter(System.out));
}
else {
System.out.println("usage: JobTracker [-dumpConfiguration]");
System.exit(-1);
}
}
} catch (Throwable e) {
LOG.fatal(StringUtils.stringifyException(e));
System.exit(-1);
}
}
进入到startTracker函数,经过几次默认参数的调用最后到达的startTracker如下
public static JobTracker startTracker(JobConf conf, String identifier, boolean initialize)
throws IOException, InterruptedException {
DefaultMetricsSystem.initialize("JobTracker");
JobTracker result = null;
while (true) {
try {
result = new JobTracker(conf, identifier); //JobTracker构造函数,其中identifier是通过日期产生的
result.taskScheduler.setTaskTrackerManager(result); //将taskScheduler的taskTrackerManager设置为JobTracker自身,这个
//的作用在后面的代码中会说明
break;
} catch (VersionMismatch e) {
throw e;
} catch (BindException e) {
throw e;
} catch (UnknownHostException e) {
throw e;
} catch (AccessControlException ace) {
// in case of jobtracker not having right access
// bail out
throw ace;
} catch (IOException e) {
LOG.warn("Error starting tracker: " +
StringUtils.stringifyException(e));
}
Thread.sleep(1000);
}
if (result != null) {
JobEndNotifier.startNotifier(); //开启一个Job结束的通知器,其实内部是开一个线程观察一个BlockingQueue<jobEndStatusInfo>的队列,当TaskTracker通过RPC告诉JobTracker Job的运行状态后,如果结束就会在某个地方往这个队列里面加入任务的结束状态信息,然后
该观察线程就会发出HttpNotification,而接受这个Notification的应该是JobTracker内部的一个HttpServer
MBeans.register("JobTracker", "JobTrackerInfo", result);
if(initialize == true) {
result.setSafeModeInternal(SafeModeAction.SAFEMODE_ENTER);
result.initializeFilesystem(); //初始化文件系统,其实就是通过`return FileSystem.get(conf);`返回一个文件系统类,内部有一个缓存,首先判断缓存中是否有Key(uri,conf)对象的文件系统类,有就直接返回,否则通过反射生成一个。uri是conf中fs.defult.name的属性值,如果该值没有设置则使用file:///
result.setSafeModeInternal(SafeModeAction.SAFEMODE_LEAVE);
result.initialize(); //初始化,主要是对JobHistory做了初始化,然后设置了httpserver的属性,并且开启了一个HDFS monitro线程,这里不是这次的关注点,可以略过
}
}
return result;
}
上个代码段中很重要的一个调用就是JobTracker的构造函数,几个重要对象例如taskScheduler和interTrackerServer以及HttpServer都是在里面实例化的。
Class<? extends TaskScheduler> schedulerClass
= conf.getClass("mapred.jobtracker.taskScheduler",
JobQueueTaskScheduler.class, TaskScheduler.class); //其实就是看conf里面有没有配置不同的TaskScheduler,系统默认的是采用
//JobQueueTaskScheduler,这个也是这次讲的时候也以它为例说明
taskScheduler = (TaskScheduler) ReflectionUtils.newInstance(schedulerClass, conf); //通过反射实例化taskScheduler
int handlerCount = conf.getInt("mapred.job.tracker.handler.count", 10); // Handler的个数,默认10个,Handler后面会降到,这里先略过
this.interTrackerServer =
RPC.getServer(this, addr.getHostName(), addr.getPort(), handlerCount,
false, conf, secretManager); //interTrackerServer是一个RPC server,它内部有listener, reader ,responder, handler这样几个比较重要的对象,用来接受处理和响应RPC请求。
infoServer = new HttpServer("job", infoBindAddress, tmpInfoPort,
tmpInfoPort == 0, conf, aclsManager.getAdminsAcl()); //HttpServer,也就是通过50070端口可以访问到JObTracker信息的server
infoServer.setAttribute("job.tracker", this); //不是这次的说明范围内,大致知道它是一个httpserver就行了
infoServer.addServlet("reducegraph", "/taskgraph", TaskGraphServlet.class);
infoServer.start();
JobTracker中的几个重要对象
taskScheduler:任务调度器,这里以默认的JobQueueTaskScheduler来说明其作用,JobQueueTaskScheduler内部会初始化JobQueueJobInProgressListener和EagerTaskInitializationListener,其中EagerTaskInitializationListener会开启内部的一个初始化线程监听JobInitQueue,然后如果发现队列里面有待初始化的Job则取出然后调用了ttm(task tracker manager)的initJob函数,其实会发现这个ttm就是jobtracker本身,这样就会调用jobtracker的initJob函数。而jobQueueJobInProgressListener则是在jobtracker接受到client端的submitjob请求的时候会调用jobAdded添加job到jobQUeue中然后在tasktracker与jobtracker的心跳中,分发任务的时候会从jobQUeue中取出来。
intertrackerServer:其实就是一个RPC的server,但是里面通过了listener、reader、handler、responder这几个对象和NIO实现了非阻塞异步IO。其中listener主要用来监听do_accept,reader监听do_read,handler处理rpc请求内容,responder负责根据调用结果返回给client
HttpServer: 这个就是jobtracker web接口的server,这里没有具体去分析。
intertackerServer分析
intertrackerServer的启动在offerService函数中,intertrackerServer.start()
,其实就是开启一个rpc server。要理解intertackerServer干了些什么,这个rpc server是如何接受和处理请求的,得从intertrackerServer = RPC.getServer
看起
public static Server getServer(final Object instance, final String bindAddress, final int port,
final int numHandlers,
final boolean verbose, Configuration conf)
throws IOException {
return getServer(instance, bindAddress, port, numHandlers, verbose, conf, null);
}
/** Construct a server for a protocol implementation instance listening on a
* port and address, with a secret manager. */
public static Server getServer(final Object instance, final String bindAddress, final int port,
final int numHandlers,
final boolean verbose, Configuration conf,
SecretManager<? extends TokenIdentifier> secretManager)
throws IOException {
return new Server(instance, conf, bindAddress, port, numHandlers, verbose, secretManager);
}
//这个Server函数是RPC类中的内部类Server,它的super类是ipc.Server
public Server(Object instance, Configuration conf, String bindAddress, int port,
int numHandlers, boolean verbose,
SecretManager<? extends TokenIdentifier> secretManager)
throws IOException {
super(bindAddress, port, Invocation.class, numHandlers, conf,
classNameBase(instance.getClass().getName()), secretManager); //第三个参数Incocation.class需要注意,它是rpc中的调用信息 //的存储类,在这个调用中会实例化listener和responder
this.instance = instance;
this.verbose = verbose;
}
其实可以看到,getServer最后会调用new Server生成一个rpcserver,new Server(instance, conf, bindAddress,port, numHandlers,verbose,secretManager)
这个函数需要注意的是第一个参数instance,它其实就是jobtracker的实例,在RPC.Call函数中将要用到这个变量。
在jobTracker的 offerservice函数中会调用interTrackerServer.start函数,由于RPC.Server继承了ipc.Server,其实就是调用ipc.Server的start方法如下: