[In-depth explanation of Yarn architecture and implementation] 2-3 Yarn basic library-service library and event library

In a huge distributed system, how do the various components coordinate their work? How are components decoupled? How can threads run more efficiently and reduce inefficiencies caused by blocking? This section will introduce Yarn's service library and event library, and see how Yarn solves these problems.

1. Service library

1) Introduction

For objects with a long life cycle, Yarn adopts a service-based model to manage them, which has the following characteristics:

  • State-based management: divided into 4 states: NOTINITED(created), INITED(initialized), STARTED(started), STOPPED(stopped).
  • Changes in the state of the service trigger other actions.
  • Services can be composed in a compositional manner.

2) Brief analysis of source code

The source code address is hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/servicein Servicethe interface of .
It defines the four states of the service, as well as the state transition, information acquisition, registration and other methods that need to be implemented.

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);
  // ......

The abstract class AbstractServiceimplements Servicethe interface and provides the basic Serviceimplementation . Non-composition services can directly inherit this abstract class and then develop it.

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);
        }
      }
    }
  }

  // ......

For combined services such as ResourceManager, NodeManager, etc., inheritance is required CompositeService. There will be logic processing for composite services.

  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);
    }
  }

2. Event library

The problem with traditional function calls:
the entire execution process is serial and synchronous. When calling another function, you need to wait for the function to complete before continuing. The schematic diagram is as follows:
image.png

In order to solve the problem of functional calls, the programming model of **"event-driven"** can be used.

  • All objects are abstracted into event handlers
  • Event handlers are associated with each other through events
  • Each event handler handles an event
  • Another event is fired as needed
  • The processing of each type of event can be divided into multiple steps, represented by a finite state machine
  • The important thing is that there is a **"Central Asynchronous Dispatcher (AsyncDispatcher)", ** responsible for collecting and distributing events to be processed

The schematic diagram is as follows:
image.png

Through the above method, the program can have the characteristics of low coupling and high cohesion. Each module only needs to complete its own function, and at the same time improve the execution efficiency. The split operation can be sent out through events.

3. Use cases of service library and event library

This section will implement a simplified version MapReduce ApplicationMasterto help understand how to use service and event.
Similar to MR, a job will be divided into multiple tasks for execution. So events involving both job and task objects. And have a AsyncDispatcherhandle dispatch.
The case has been uploaded to github, please click ⭐️
https://github.com/Simon-Ace/hadoop-yarn-study-demo/tree/master/service-event-demo

a) Events section

Refer to the implementation of Task and Job Event in hadoop source code to make some simplifications.
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;
    }
}

2) Event Scheduler

  • Define and register EventDispatcher
  • service initialization and start method
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());
            }
        }
    }
}

3) Test procedure

  • generate a new job
  • trigger event JOB_KILLandJOB_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));
    }
}

Output result:

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

Four. Summary

This section introduces Yarn's service and event libraries.
The service library standardizes service-type objects with a long life cycle, defines the four states of the service, the methods to be realized such as start-stop registration, and gives the basic implementation of single-type and combined-type services.
The use of the event library solves the problems of high coupling and blocking inefficiency of the original function call. A large task can be split into multiple small tasks, and small tasks become different events to trigger processing. Each event handler handles one type of event, and a central asynchronous scheduler manages event collection and distribution.
Finally, a simplified MR ApplicationMaster is used to combine the event library and the service library, so as to gain a deeper understanding of how to use them together in the project.
During the learning process, writing a demo can better help you understand the knowledge.


Reference article:
"Hadoop Technology Insider - In-depth Analysis of Yarn Structure Design and Implementation Principles" Section 3.4

Guess you like

Origin blog.csdn.net/shuofxz/article/details/127308665