1.JobManager启动流程解析.md

JobManager启动流程解析

本文档采取总分总的方式,先介绍JobManager的启动顺序,再从启动顺序中拆解每个组件最底层的实现。最后再总结一次JobManager的实现。

本文主要介绍standalone模式下的JobManager实现。大部分组件的实现都类似,只是在高可用、executeGraph上会有实现的差异。

本文的主题是:JobManager 启动时,如何把集群的三大核心组件拉起来 ------DispatcherResourceManagerWebRestEndpoint。可以把 JobManager 的启动理解为"先把地基服务准备好,再把三大组件创建并启动"。

  • ResourceManager(资源管理):负责集群资源的生命周期管理与分配,例如 TaskManager 的注册与心跳、Slot 资源的管理与分配,以及与外部资源提供者(Standalone/YARN/K8s 等)交互以申请或释放资源。
  • Dispatcher(作业调度入口) :负责作业提交入口与调度编排,接收作业提交请求,创建/恢复 JobMaster(每个作业一个),并维护作业/集群相关的运行信息与对外服务的网关。
  • WebRestEndpoint(对外服务入口):提供 Web UI 与 REST API 能力,对外暴露集群与作业的查询、运维与控制接口(例如作业提交、取消、查询状态、拉取 metrics 等)。

在启动三大组件之前,需要先初始化一组"基础服务组件",为后续组件创建提供运行环境与依赖:

  • BlobServer(作业/依赖分发服务):负责作业 Jar、UDF、依赖包等文件的上传、缓存与分发。
  • RPC 通信服务(commonRpcService):三大组件之间以及与 TaskManager 等进程之间的核心通信基础设施。
  • 指标注册/查询服务(MetricRegistry & MetricQueryService):指标采集、注册、上报与查询的统一入口,支撑 Web/REST 的 metrics 展示与查询。
  • 以及 HA/心跳/工作目录/安全令牌 等支撑性能力(standalone 下多为默认实现,但生命周期与依赖关系一致)。

1. 启动入口与类图分析

首先找到 Standalone 启动类,路径位于:

`StandaloneSessionClusterEntrypoint.java`\](file:///c:/Users/Administrator/Desktop/flink-sourcecode/flink-runtime/src/main/java/org/apache/flink/runtime/entrypoint/StandaloneSessionClusterEntrypoint.java) **类继承关系(UML图):** implements implements extends extends \<\\> AutoCloseableAsync \<\\> FatalErrorHandler ClusterEntrypoint SessionClusterEntrypoint StandaloneSessionClusterEntrypoint * **AutoCloseableAsync** :实现了 `AutoCloseable` 接口,用于异步关闭资源。 * **FatalErrorHandler**:用于处理致命错误,如内存溢出、线程死亡等。 * **ClusterEntrypoint**:集群启动基类。内部初始化了 JobManager 集群所需的核心组件。 * **StandaloneSessionClusterEntrypoint** :继承了 `ClusterEntrypoint`。其 `main` 方法是整个集群的启动入口。 *** ** * ** *** ### 2. 核心启动流程源码拆解 先用"流程视角"总结一下 JobManager 的核心启动顺序(standalone session 模式): * **启动入口阶段(main)**:打印环境信息 → 注册信号接收器(用于优雅退出/打印结束日志)→ 解析启动参数并生成集群配置 → 初始化集群入口实例 → 启动集群入口。 * **基础环境准备(startCluster)** :注册退出机制(拦截用户代码的 `System.exit`)→ 初始化插件管理 → 初始化文件系统 → 安全上下文(Kerberos 等)→ 进入受控环境后启动集群主流程。 * **运行集群(runCluster)** :先初始化所有基础服务(`initializeServices`)→ 再创建并启动三大核心组件(Dispatcher / ResourceManager / WebRestEndpoint)→ 注册 shutdown 回调,阻止主线程退出,直到应用最终状态完成。 生命周期与退出 注册 shutdown 回调 等待 getShutDownFuture ApplicationStatus 最终退出 核心组件启动 Dispatcher ResourceManager WebRestEndpoint 基础服务初始化(initializeServices) 资源与目录:resourceId / workingDirectory RPC:commonRpcService 线程池:ioExecutor 安全令牌:DelegationToken HA:haServices BlobServer:作业/依赖分发 HeartbeatServices:心跳 Metrics:MetricRegistry / QueryService ExecutionGraphInfoStore 基础服务组件初始化(startCluster) 注册退出机制(拦截 System.exit) 插件管理 初始化文件系统 安装安全上下文(Kerberos 等) runCluster 初次启动(main) 打印环境信息 注册信号接收器 / Shutdown Hook 解析参数并生成 Configuration 创建 StandaloneSessionClusterEntrypoint runClusterEntrypoint → startCluster JobManager 的启动流程主要围绕以下四个核心方法展开: #### 2.1 StandaloneSessionClusterEntrypoint.main 作为启动入口,主要负责解析配置、注册信号处理、初始化入口类实例,并调用基类的启动方法。 ```java public static void main(String[] args) { // 1. startup checks and logging (打印环境信息) EnvironmentInformation.logEnvironmentInfo( LOG, StandaloneSessionClusterEntrypoint.class.getSimpleName(), args); // 2. 信号注册。JobManager接收信号 SignalHandler.register(LOG); JvmShutdownSafeguard.installAsShutdownHook(LOG); // 3. 创建集群入口配置。这里面只包含了Java启动的传参。 final EntrypointClusterConfiguration entrypointClusterConfiguration = // 这个函数会解析参数,返回一个EntrypointClusterConfiguration(里面包含了flink运行需要的所有参数) ClusterEntrypointUtils.parseParametersOrExit( args, new EntrypointClusterConfigurationParserFactory(), StandaloneSessionClusterEntrypoint.class); // 4. 进一步根据传参创建集群配置 Configuration configuration = loadConfiguration(entrypointClusterConfiguration); // 5. 实例化 StandaloneSessionClusterEntrypoint StandaloneSessionClusterEntrypoint entrypoint = new StandaloneSessionClusterEntrypoint(configuration); // 6. 调用基类的静态启动方法 ClusterEntrypoint.runClusterEntrypoint(entrypoint); } ``` #### 2.2 ClusterEntrypoint.runClusterEntrypoint 这是一个静态帮助方法,用于包裹实际的启动逻辑,并处理启动失败和集群退出的异常。 ```java public static void runClusterEntrypoint(ClusterEntrypoint clusterEntrypoint) { final String clusterEntrypointName = clusterEntrypoint.getClass().getSimpleName(); try { // 调用实例的 startCluster 方法启动集群 clusterEntrypoint.startCluster(); } catch (ClusterEntrypointException e) { LOG.error( String.format("Could not start cluster entrypoint %s.", clusterEntrypointName), e); System.exit(STARTUP_FAILURE_RETURN_CODE); } int returnCode; Throwable throwable = null; try { // 阻塞等待集群终止,获取退出码 returnCode = clusterEntrypoint.getTerminationFuture().get().processExitCode(); } catch (Throwable e) { throwable = ExceptionUtils.stripExecutionException(e); returnCode = RUNTIME_FAILURE_RETURN_CODE; } LOG.info( "Terminating cluster entrypoint process {} with exit code {}.", clusterEntrypointName, returnCode, throwable); System.exit(returnCode); } ``` #### 2.3 ClusterEntrypoint.startCluster 该方法负责集群的基础环境准备,包括安全配置、插件管理、文件系统初始化,并最终在安全上下文中调用 `runCluster`。 ```java public void startCluster() throws ClusterEntrypointException { LOG.info("Starting {}.", getClass().getSimpleName()); try { // 1. 注册退出机制策略,确保用户代码的 System.exit 被拦截 FlinkSecurityManager.setFromConfiguration(configuration); // 2. 初始化插件管理器 PluginManager pluginManager = PluginUtils.createPluginManagerFromRootFolder(configuration); // 3. 对文件系统进行初始化。主要依靠 configuration 和 pluginManager configureFileSystems(configuration, pluginManager); // 4. 初始化 security context。Kerberos 是大数据安全认证协议。 SecurityContext securityContext = installSecurityContext(configuration); // 5. 确保未捕获异常,只打印,不退出。 ClusterEntrypointUtils.configureUncaughtExceptionHandler(configuration); // 6. 在安全上下文中运行集群 securityContext.runSecured( (Callable) () -> { // 运行集群核心逻辑 runCluster(configuration, pluginManager); return null; }); } catch (Throwable t) { final Throwable strippedThrowable = ExceptionUtils.stripException(t, UndeclaredThrowableException.class); // 异常处理与资源清理逻辑... throw new ClusterEntrypointException( String.format( "Failed to initialize the cluster entrypoint %s.", getClass().getSimpleName()), strippedThrowable); } } ``` #### 2.4 ClusterEntrypoint.initializeServices 在 `runCluster` 方法中,最先调用的就是 `initializeServices`。这个方法负责初始化 JobManager 运行所需的各种基础服务组件,如 RPC 系统、高可用服务、心跳服务、BlobServer(文件分发)和 Metric 监控等。 ```java protected void initializeServices(Configuration configuration, PluginManager pluginManager) throws Exception { LOG.info("Initializing cluster services."); synchronized (lock) { // 1. 获取jobmanager的resourceId resourceId = configuration .getOptional(JobManagerOptions.JOB_MANAGER_RESOURCE_ID) .map( value -> DeterminismEnvelope.deterministicValue( new ResourceID(value))) .orElseGet( () -> DeterminismEnvelope.nondeterministicValue( ResourceID.generate())); LOG.debug( "Initialize cluster entrypoint {} with resource id {}.", getClass().getSimpleName(), resourceId); // 2. 创建 JobManager 的工作目录 workingDirectory = ClusterEntrypointUtils.createJobManagerWorkingDirectory( configuration, resourceId); LOG.info("Using working directory: {}.", workingDirectory); // 3. rpcsystem 系统被初始化 rpcSystem = RpcSystem.load(configuration); // 4. 最基础的rpc通信服务 (基于 Akka 或其他 RPC 实现) commonRpcService = RpcUtils.createRemoteRpcService( rpcSystem, configuration, configuration.get(JobManagerOptions.ADDRESS), getRPCPortRange(configuration), configuration.get(JobManagerOptions.BIND_HOST), configuration.getOptional(JobManagerOptions.RPC_BIND_PORT)); // 5. JMX监控,主要是jvm信息 JMXService.startInstance(configuration.get(JMXServerOptions.JMX_SERVER_PORT)); // 更新配置中的地址和端口,供后续 HA 服务使用 configuration.set(JobManagerOptions.ADDRESS, commonRpcService.getAddress()); configuration.set(JobManagerOptions.PORT, commonRpcService.getPort()); // 6. 初始化固定大小的IO线程池 ioExecutor = Executors.newFixedThreadPool( ClusterEntrypointUtils.getPoolSize(configuration), new ExecutorThreadFactory("cluster-io")); // 7. 初始化安全令牌管理器 (Delegation Token) delegationTokenManager = DefaultDelegationTokenManagerFactory.create( configuration, pluginManager, commonRpcService.getScheduledExecutor(), ioExecutor); // 获取令牌并传递给本地 JVM,这是因为 BlobServer 等组件可能需要连接外部安全文件系统 delegationTokenManager.obtainDelegationTokens(); // 8. HA高可用服务初始化。这里默认是standalone模式,所以用的是StandaloneHaServices haServices = createHaServices(configuration, ioExecutor, rpcSystem); // 9. 作业,udf等文件分发服务。初始化 BlobServer 并启动 blobServer = BlobUtils.createBlobServer( configuration, Reference.borrowed(workingDirectory.unwrap().getBlobStorageDirectory()), haServices.createBlobStore()); blobServer.start(); // 调用完后,将实际绑定的端口设置回配置中 configuration.set(BlobServerOptions.PORT, String.valueOf(blobServer.getPort())); // 10. 心跳初始化服务 heartbeatServices = createHeartbeatServices(configuration); // 11. Failure 增强接口,用于对外暴露和丰富失败信息 failureEnrichers = FailureEnricherUtils.getFailureEnrichers(configuration); // 12. 监控 MetricRegistry 注册 metricRegistry = createMetricRegistry(configuration, pluginManager, rpcSystem); // 创建并启动 metricQueryServiceRpcService final RpcService metricQueryServiceRpcService = MetricUtils.startRemoteMetricsRpcService( configuration, commonRpcService.getAddress(), configuration.get(JobManagerOptions.BIND_HOST), rpcSystem); metricRegistry.startQueryService(metricQueryServiceRpcService, null); final String hostname = RpcUtils.getHostname(commonRpcService); // 实例化进程级别的 MetricGroup processMetricGroup = MetricUtils.instantiateProcessMetricGroup( metricRegistry, hostname, ConfigurationUtils.getSystemResourceMetricsProbingInterval( configuration)); // 13. 存储 ExecutionGraph 的组件(如 MemoryExecutionGraphInfoStore) executionGraphInfoStore = createSerializableExecutionGraphStore( configuration, commonRpcService.getScheduledExecutor()); } } ``` #### 2.5 ClusterEntrypoint.runCluster 这里是真正的 JobManager 核心组件初始化的地方。在调用 `initializeServices` 初始化基础服务(如 HA、RPC通信)之后,会创建和启动三大核心组件:`Dispatcher`、`ResourceManager` 和 `WebMonitorEndpoint`。 ```java private void runCluster(Configuration configuration, PluginManager pluginManager) throws Exception { synchronized (lock) { // 1. 先初始化最基础的服务组件。比如高可用,心跳,blob服务,rpc通信等。 initializeServices(configuration, pluginManager); // write host information into configuration configuration.set(JobManagerOptions.ADDRESS, commonRpcService.getAddress()); configuration.set(JobManagerOptions.PORT, commonRpcService.getPort()); // 2. 创建三大组件的工厂类 // - dispatcher组件 // - resourceManager组件 // - webRestEndpoint组件 final DispatcherResourceManagerComponentFactory dispatcherResourceManagerComponentFactory = createDispatcherResourceManagerComponentFactory(configuration); // 3. 利用工厂类,传入基础服务,创建并启动三大核心组件 clusterComponent = dispatcherResourceManagerComponentFactory.create( configuration, // 集群配置 resourceId.unwrap(), // jobmanager id ioExecutor, // 线程池 commonRpcService, // rpc服务 haServices, // 高可用服务 blobServer, // blob服务 heartbeatServices, // 心跳服务 delegationTokenManager, // token令牌,主要是用户认证 metricRegistry, // metric服务 executionGraphInfoStore, // 作业执行图 new RpcMetricQueryServiceRetriever( // 构造metric查询服务网关 metricRegistry.getMetricQueryServiceRpcService()), failureEnrichers, // 失败增强 this); // 退出作业接口 // 4. 注册关闭回调:获取app的最终运行状态并关闭集群 // whenComplete只有异步任务完成后才会调用,确保了集群不会直接退出 clusterComponent .getShutDownFuture() .whenComplete( (ApplicationStatus applicationStatus, Throwable throwable) -> { if (throwable != null) { shutDownAsync( ApplicationStatus.UNKNOWN, ShutdownBehaviour.GRACEFUL_SHUTDOWN, ExceptionUtils.stringifyException(throwable), false); } else { shutDownAsync( applicationStatus, ShutdownBehaviour.GRACEFUL_SHUTDOWN, null, true); } }); } } ```

相关推荐
Elastic 中国社区官方博客5 小时前
使用 Jina 远程 MCP 服务器的 Agentic 工作流
大数据·运维·人工智能·elasticsearch·搜索引擎·运维开发·jina
金融小师妹5 小时前
AI宏观情景模型解析:能源冲击与通胀粘性下的全球经济增长下修路径
大数据·svn·逻辑回归·能源
上海锝秉工控7 小时前
总线编码器:工业自动化的“智慧神经”
大数据·人工智能·自动化
互联网科技看点7 小时前
AtlasX Protocol 获 200 万美元种子轮融资
大数据·人工智能·区块链
观远数据7 小时前
AI优先的BI试点新玩法:如何用自然语言分析重构业务决策流程
大数据·人工智能·数据挖掘
ApacheSeaTunnel7 小时前
Apache SeaTunnel Zeta 为什么能做到“又快又稳”?
大数据·开源·数据集成·seatunnel·技术分享·数据同步
源码宝8 小时前
新一代医院信息系统云HIS,多租户共享,java版HIS+EMR+LIS全套源码
java·大数据·源码·云his·his系统·源代码·医院信息系统
跨境卫士-小汪9 小时前
多渠道获客复杂化跨境卖家如何优化整体结构
大数据·人工智能·产品运营·跨境电商·跨境
研究点啥好呢9 小时前
Github热门项目推荐 | 开放数据的新时代
大数据·人工智能·机器学习·github·数据