【深入浅出 Yarn 架构与实现】2-3 Yarn 基础库 - 服务库与事件库
一个庞大的分布式系统,各个组件间是如何协调工作的?组件是如何解耦的?线程运行如何更高效,减少阻塞带来的低效问题?本节将对 Yarn 的服务库和事件库进行介绍,看看 Yarn 是如何解决这些问题的。
一、服务库
一)简介
对于生命周期较长的对象,Yarn 采用基于服务的模型对其进行管理,有以下几个特点:
- 基于状态管理:分为 4 个状态:
NOTINITED(被创建)、INITED(已初始化)、STARTED(已启动)、STOPPED(已停止)。 - 服务状态的变化会触发其他的操作。
- 可通过组合的方式对服务进行组合。
二)源码简析
源代码地址在 hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/service 的 Service 接口中。
其中定义了服务的四个状态,以及需要实现的状态转换、获取信息、注册等方法。
public interface Service extends Closeable {
public enum STATE {
NOTINITED(0, "NOTINITED"),
INITED(1, "INITED"),
STARTED(2, "STARTED"),
STOPPED(3, "STOPPED");
}
void init(Configuration config);
void start();
void stop();
void close() throws IOException;
void registerServiceListener(ServiceStateChangeListener listener);
// ......
抽象类 AbstractService 实现了 Service 接口,提供了基础的 Service 实现,非组合服务直接继承这个抽象类再开发即可。
public abstract class AbstractService implements Service {
// 以 start 实现为例,执行后会触发其他的操作
public void start() {
if (isInState(STATE.STARTED)) {
return;
}
//enter the started state
synchronized (stateChangeLock) {
if (stateModel.enterState(STATE.STARTED) != STATE.STARTED) {
try {
startTime = System.currentTimeMillis();
serviceStart();
if (isInState(STATE.STARTED)) {
//if the service started (and isn't now in a later state), notify
if (LOG.isDebugEnabled()) {
LOG.debug("Service " + getName() + " is started");
}
notifyListeners();
}
} catch (Exception e) {
noteFailure(e);
ServiceOperations.stopQuietly(LOG, this);
throw ServiceStateException.convert(e);
}
}
}
}
// ......
对于组合类的服务如 ResourceManager、NodeManager 等,需要继承 CompositeService。其中会有对组合服务的逻辑处理。
public List<Service> getServices() {
synchronized (serviceList) {
return new ArrayList<Service>(serviceList);
}
}
protected void addService(Service service) {
if (LOG.isDebugEnabled()) {
LOG.debug("Adding service " + service.getName());
}
synchronized (serviceList) {
serviceList.add(service);
}
}
二、事件库
传统函数式调用的问题:
整个执行过程是串行、同步进行的。调用另一个函数的时候,需要等待函数执行完毕,才会继续往下走。示意图如下:

为了解决函数式调用的问题,可使用「事件驱动」的编程模型。
- 所有对象都被抽象成事件处理器
- 事件处理器之间通过事件相关联
- 每种事件处理器处理一种事件
- 根据需要会触发另一种事件
- 每类事件的处理可分割为多个步骤,用有限状态机表示
- 重要的是有一个「中央异步调度器(AsyncDispatcher)」,负责对待处理事件的收取和分发
示意图如下:

通过以上的方式,可以使程序有低耦合高内聚的特点,各个模块仅需完成各自的功能,同时提高了执行效率,把拆分的操作通过事件的方式发送出去即可。
三、服务库和事件库使用案例
本节将实现一个简化版的 MapReduce ApplicationMaster,帮助了解 service 和 event 的使用方法。
与 MR 类似,一个 job 将被分为多个 task 执行。因此涉及 job 和 task 两种对象的事件。并有一个 AsyncDispatcher 处理调度。
案例已上传至 github,有帮助可以点个 ️
https://github.com/Simon-Ace/hadoop-yarn-study-demo/tree/master/service-event-demo
一)事件部分
参考 hadoop 源码中 Task 和 Job Event 的实现,进行一些简化。
1、task
public enum TaskEventType {
//Producer:Client, Job
T_KILL,
//Producer:Job
T_SCHEDULE
}
public class TaskEvent extends AbstractEvent<TaskEventType> {
private String taskID;
public TaskEvent(String taskID, TaskEventType type) {
super(type);
this.taskID = taskID;
}
public String getTaskID() {
return taskID;
}
}
2、job
public enum JobEventType {
//Producer:Client
JOB_KILL,
//Producer:MRAppMaster
JOB_INIT
}
public class JobEvent extends AbstractEvent<JobEventType> {
private String jobID;
public JobEvent(String jobID, JobEventType type) {
super(type);
this.jobID = jobID;
}
public String getJobId() {
return jobID;
}
}
二)事件调度器
- 定义和注册 EventDispatcher
- service 初始化和启动方法
import com.shuofxz.event.JobEvent;
import com.shuofxz.event.JobEventType;
import com.shuofxz.event.TaskEvent;
import com.shuofxz.event.TaskEventType;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.service.CompositeService;
import org.apache.hadoop.service.Service;
import org.apache.hadoop.yarn.event.AsyncDispatcher;
import org.apache.hadoop.yarn.event.Dispatcher;
import org.apache.hadoop.yarn.event.EventHandler;
@SuppressWarnings("unchecked")
public class MyMRAppMaster extends CompositeService {
private Dispatcher dispatcher; // AsyncDispatcher
private String jobID;
private int taskNumber; // 一个 job 包含的 task 数
private String[] taskIDs;
public MyMRAppMaster(String name, String jobID, int taskNumber) {
super(name);
this.jobID = jobID;
this.taskNumber = taskNumber;
taskIDs = new String[taskNumber];
for (int i = 0; i < taskNumber; i++) {
taskIDs[i] = this.jobID + "_task_" + i;
}
}
public void serviceInit(Configuration conf) throws Exception {
dispatcher = new AsyncDispatcher();
dispatcher.register(JobEventType.class, new JobEventDispatcher()); // register a job
dispatcher.register(TaskEventType.class, new TaskEventDispatcher()); // register a task
addService((Service) dispatcher);
super.serviceInit(conf);
}
public void serviceStart() throws Exception {
super.serviceStart();
}
public Dispatcher getDispatcher() {
return dispatcher;
}
private class JobEventDispatcher implements EventHandler<JobEvent> {
public void handle(JobEvent event) {
if (event.getType() == JobEventType.JOB_KILL) {
System.out.println("Receive JOB_KILL event, killing all the tasks");
for (int i = 0; i < taskNumber; i++) {
dispatcher.getEventHandler().handle(new TaskEvent(taskIDs[i], TaskEventType.T_KILL));
}
} else if (event.getType() == JobEventType.JOB_INIT) {
System.out.println("Receive JOB_INIT event, scheduling tasks");
for (int i = 0; i < taskNumber; i++) {
dispatcher.getEventHandler().handle(new TaskEvent(taskIDs[i], TaskEventType.T_SCHEDULE));
}
}
}
}
private class TaskEventDispatcher implements EventHandler<TaskEvent> {
public void handle(TaskEvent event) {
if (event.getType() == TaskEventType.T_KILL) {
System.out.println("Receive T_KILL event of task id " + event.getTaskID());
} else if (event.getType() == TaskEventType.T_SCHEDULE) {
System.out.println("Receive T_SCHEDULE event of task id " + event.getTaskID());
}
}
}
}
三)测试程序
- 生成一个新的 job
- 触发事件
JOB_KILL和JOB_INIT
public class MyMRAppMasterTest {
public static void main(String[] args) {
String jobID = "job_20221011_99";
MyMRAppMaster appMaster = new MyMRAppMaster("My MRAppMaster Test", jobID, 10);
YarnConfiguration conf = new YarnConfiguration(new Configuration());
try {
appMaster.serviceInit(conf);
appMaster.serviceStart();
} catch (Exception e) {
e.printStackTrace();
}
appMaster.getDispatcher().getEventHandler().handle(new JobEvent(jobID, JobEventType.JOB_KILL));
appMaster.getDispatcher().getEventHandler().handle(new JobEvent(jobID, JobEventType.JOB_INIT));
}
}
输出结果:
Receive JOB_KILL event, killing all the tasks
Receive JOB_INIT event, scheduling tasks
Receive T_KILL event of task id job_20150723_11_task_0
Receive T_KILL event of task id job_20150723_11_task_1
Receive T_KILL event of task id job_20150723_11_task_2
Receive T_KILL event of task id job_20150723_11_task_3
Receive T_KILL event of task id job_20150723_11_task_4
Receive T_KILL event of task id job_20150723_11_task_5
Receive T_KILL event of task id job_20150723_11_task_6
Receive T_KILL event of task id job_20150723_11_task_7
Receive T_KILL event of task id job_20150723_11_task_8
Receive T_KILL event of task id job_20150723_11_task_9
Receive T_SCHEDULE event of task id job_20150723_11_task_0
Receive T_SCHEDULE event of task id job_20150723_11_task_1
Receive T_SCHEDULE event of task id job_20150723_11_task_2
Receive T_SCHEDULE event of task id job_20150723_11_task_3
Receive T_SCHEDULE event of task id job_20150723_11_task_4
Receive T_SCHEDULE event of task id job_20150723_11_task_5
Receive T_SCHEDULE event of task id job_20150723_11_task_6
Receive T_SCHEDULE event of task id job_20150723_11_task_7
Receive T_SCHEDULE event of task id job_20150723_11_task_8
Receive T_SCHEDULE event of task id job_20150723_11_task_9
四、总结
本节介绍了 Yarn 的服务和事件库。
服务库规范了生命周期较长的服务型对象,定义了服务的四种状态、启停注册等要实现的方法,给出了单一类型和组合类型服务的基本实现。
事件库的使用,解决了原始函数型调用的高耦合、阻塞低效等问题。可将一个大任务拆分成多个小任务,小任务变成不同的事件来触发处理。每一个事件处理器处理一种事件,并有一个中央异步调度器管理事件的收集和分发。
最后用一个简化的 MR ApplicationMaster 将事件库和服务库进行结合,更深体会如何在项目中将其结合使用。
学习过程中,写一个 demo 能更好的帮助你理解知识。
参考文章:
《Hadoop 技术内幕 - 深入解析 Yarn 结构设计与实现原理》3.4 节
【深入浅出 Yarn 架构与实现】2-3 Yarn 基础库 - 服务库与事件库的更多相关文章
- 【深入浅出 Yarn 架构与实现】3-1 Yarn Application 流程与编写方法
本篇学习 Yarn Application 编写方法,将带你更清楚的了解一个任务是如何提交到 Yarn ,在运行中的交互和任务停止的过程.通过了解整个任务的运行流程,帮你更好的理解 Yarn 运作方式 ...
- 【深入浅出 Yarn 架构与实现】2-2 Yarn 基础库 - 底层通信库 RPC
RPC(Remote Procedure Call) 是 Hadoop 服务通信的关键库,支撑上层分布式环境下复杂的进程间(Inter-Process Communication, IPC)通信逻辑, ...
- 【深入浅出 Yarn 架构与实现】2-1 Yarn 基础库概述
了解 Yarn 基础库是后面阅读 Yarn 源码的基础,本节对 Yarn 基础库做总体的介绍.并对其中使用的第三方库 Protocol Buffers 和 Avro 是什么.怎么用做简要的介绍. 一. ...
- 【深入浅出 Yarn 架构与实现】2-4 Yarn 基础库 - 状态机库
当一个服务拥有太多处理逻辑时,会导致代码结构异常的混乱,很难分辨一段逻辑是在哪个阶段发挥作用的. 这时就可以引入状态机模型,帮助代码结构变得清晰. 一.状态机库概述 一)简介 状态机由一组状态组成: ...
- 【深入浅出 Yarn 架构与实现】1-2 搭建 Hadoop 源码阅读环境
本文将介绍如何使用 idea 搭建 Hadoop 源码阅读环境.(默认已安装好 Java.Maven 环境) 一.搭建源码阅读环境 一)idea 导入 hadoop 工程 从 github 上拉取代码 ...
- 【深入浅出 Yarn 架构与实现】1-1 设计理念与基本架构
一.Yarn 产生的背景 Hadoop2 之前是由 HDFS 和 MR 组成的,HDFS 负责存储,MR 负责计算. 一)MRv1 的问题 耦合度高:MR 中的 jobTracker 同时负责资源管理 ...
- Spark on Yarn 架构解析
. 一.Hadoop Yarn组件介绍: 我们都知道yarn重构根本的思想,是将原有的JobTracker的两个主要功能资源管理器 和 任务调度监控 分离成单独的组件.新的架构使用全局管理所有应用程序 ...
- Yarn集群的搭建、Yarn的架构和WordCount程序在集群提交方式
一.Yarn集群概述及搭建 1.Mapreduce程序运行在多台机器的集群上,而且在运行是要使用很多maptask和reducertask,这个过程中需要一个自动化任务调度平台来调度任务,分配资源,这 ...
- Yarn架构详解
Yarn架构介绍Yarn/MRv2最基本的想法是将原JobTracker主要的资源管理和job调度/监视功能分开作为两个单独的守护进程.有一个全局的ResourceManager(RM)和每个Appl ...
随机推荐
- RestTemplate用法
RestTemplate 用法 RestTemplate简介 RestTemplate 是一个同步的web http客户端请求模板工具,spring框架做的抽象模板, 常见的http客户端请求工具有: ...
- vivo 基于 JaCoCo 的测试覆盖率设计与实践
作者:vivo 互联网服务器团队- Xu Shen 本文主要介绍vivo内部研发平台使用JaCoCo实现测试覆盖率的实践,包括JaCoCo原理介绍以及在实践过程中遇到的新增代码覆盖率统计问题和频繁发布 ...
- pod资源的健康检查-readiness探针的httpGet使用
livenessProbe:健康状态检查,周期性检查服务是否存活,检查结果失败,将重启容器 readinessProbe:可用性检查,周期性检查服务是否可用,不可用将从service的endpoint ...
- 如何通过C#/VB.NET设置Word文档段落缩进
缩进是指调整文本与页面边界之间的距离.在水平标尺,有四个段落缩进滑块:首行缩进.悬挂缩进.左缩进以及右缩进.在对于word文档的录入时,常常需要注意录入的格式,通过合理地设置段落格式,可以让文稿看起来 ...
- 【面试题】js 问号(?)的强大之处,你知道吗??
问号(?)的强大之处 点击打开视频讲解更加详细 一.问号点(?.) obj: { name: "末晨曦吖", }, console.log(this.obj.age, " ...
- KingbaseES R6 集群repmgr.conf参数'recovery'测试案例(三)
案例三:测试'recovery = manual' 1.查看集群节点状态信息: [kingbase@node1 bin]$ ./repmgr cluster show ID | Name | Role ...
- Mysql 实现 向上递归查找父节点并返回树结构
需求:通过mysql 8.0以下版本实现,一个人多角色id,一个角色对应某个节点menu_id,根节点的父节点存储为NULL, 向上递归查找父节点并返回树结构. 如果只有叶子,剔除掉; 如果只有根,只 ...
- 内存溢出(OOM)分析
当JVM内存不足时,会抛出java.lang.OutOfMemoryError. 主要的OOM类型右: Java heap space:堆空间不足 GC overhead limit exceed ...
- 【原创】FFMPEG录屏入门指南
下载ffmpeg 点击 ffmpeg官网,选择windows,然后点击Windows builds from gyan.dev: 也可以直接点击 https://www.gyan.dev/ffmpeg ...
- 第六章:Django 综合篇 - 5:自定义django-admin命令
我们可以通过manage.py编写和注册自定义的命令. 自定义的管理命令对于独立脚本非常有用,特别是那些使用Linux的crontab服务,或者Windows的调度任务执行的脚本.比如,你有个需求,需 ...