巨大な分散システムでは、さまざまなコンポーネントがどのように作業を調整するのでしょうか? コンポーネントはどのように分離されますか? スレッドをより効率的に実行し、ブロッキングによる非効率を減らすにはどうすればよいでしょうか? このセクションでは、Yarn のサービス ライブラリとイベント ライブラリを紹介し、Yarn がこれらの問題をどのように解決するかを見ていきます。
1.サービスライブラリ
1) はじめに
ライフサイクルの長いオブジェクトの場合、Yarn はサービスベースのモデルを採用してオブジェクトを管理します。これには次の特徴があります。
- 状態ベースの管理:
NOTINITED
(作成)、INITED
(初期化)、STARTED
(開始)、STOPPED
(停止) の 4 つの状態に分割されます。 - サービスの状態が変化すると、他のアクションがトリガーされます。
- サービスは構成的な方法で構成できます。
2) ソースコードの簡単な分析
ソース コードのアドレスはhadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/service
のService
インターフェイスにあります。
サービスの 4 つの状態と、状態遷移、情報取得、登録、および実装する必要があるその他のメソッドが定義されています。
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);
}
}
2. イベントライブラリ
従来の関数呼び出しの問題点は、
実行プロセス全体がシリアルかつ同期であることです。別の関数を呼び出す場合は、関数が完了するまで待ってから続行する必要があります。概略図は次のとおりです。
関数呼び出しの問題を解決するには、**「イベント駆動型」** のプログラミング モデルを使用できます。
- すべてのオブジェクトはイベント ハンドラーに抽象化されます
- イベント ハンドラーはイベントを通じて相互に関連付けられます
- 各イベント ハンドラーはイベントを処理します
- 必要に応じて別のイベントが発生します
- 各タイプのイベントの処理は、有限状態マシンによって表される複数のステップに分割できます。
- 重要なことは、処理されるイベントの収集と配布を担当する **「中央非同期ディスパッチャー (AsyncDispatcher)」** が存在することです。
概略図は次のとおりです。
上記の方法により、プログラムは低結合かつ高凝集の特性を持ち、各モジュールは自身の機能を完了するだけでよく、同時に実行効率が向上し、分割された操作はイベントを通じて送信できます。
3. サービスライブラリとイベントライブラリのユースケース
MapReduce ApplicationMaster
このセクションでは、サービスとイベントの使用方法を理解するのに役立つ簡易バージョンを実装します。
MRと同様に、ジョブは複数のタスクに分割されて実行されます。したがって、イベントにはジョブ オブジェクトとタスク オブジェクトの両方が関係します。そしてAsyncDispatcher
ハンドル。
このケースは github にアップロードされています。⭐️
https://github.com/Simon-Ace/hadoop-yarn-study-demo/tree/master/service-event-demoをクリックしてください。
a) イベントセクション
いくつかの簡略化を行うには、Hadoop ソース コードでのタスクとジョブ イベントの実装を参照してください。
1. タスク
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、仕事
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) イベントスケジューラー
- EventDispatcherの定義と登録
- サービスの初期化と開始メソッド
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) 試験手順
- 新しい仕事を生成する
- トリガーイベント
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
4. まとめ
このセクションでは、Yarn のサービスとイベント ライブラリを紹介します。
サービスライブラリは、ライフサイクルの長いサービス型オブジェクトを標準化し、サービスの4つの状態や開始・停止登録などの実現方法を定義し、単一型サービスと複合型サービスの基本実装を提供します。
イベント ライブラリを使用すると、元の関数呼び出しの高結合と非効率性のブロックの問題が解決されます。大きなタスクは複数の小さなタスクに分割でき、小さなタスクは別のイベントとなって処理をトリガーできます。各イベント ハンドラーは 1 種類のイベントを処理し、中央の非同期スケジューラーがイベントの収集と配布を管理します。
最後に、簡素化された MR ApplicationMaster を使用してイベント ライブラリとサービス ライブラリを結合し、プロジェクト内でこれらを一緒に使用する方法をより深く理解できるようにします。
学習プロセス中にデモを作成すると、知識をよりよく理解できるようになります。
参考記事:
「Hadoop Technology Insider - Yarn 構造の設計と実装原則の詳細な分析」セクション 3.4