背景

在我们实际生产开发中,Flink作业通常以per-job的模式提交到yarn集群上运行。当作业结束或因异常退出后,此时无法从yarn web ui上查看具体的日志信息来定位异常问题;如果yarn端未开启日志聚合,yarn logs命令就无法使用了,那么我们也就无法定位问题了。

flink用户名密码 flink server_hive

flink用户名密码 flink server_flink_02

History Server简介

当相应的Flink Cluster集群down掉后,Flink提供了一个History Server服务可以查询已归档的job,同时也对外提供了接口供用户进行封装获取数据。默认情况下,该服务绑定在localhost:8082端口上,需要用户手动启动。当然也可以把该服务以独立的服务运行,接下来讲解详细配置

相关配置demo

  1. 下载Flink1.12客户端
wget https://mirror.bit.edu.cn/apache/flink/flink-1.12.0/flink-1.12.0-bin-scala_2.11.tgz
  1. 修改flink-conf.yaml文件
# 是否清理不存在的作业(即已经过期的)
historyserver.archive.clean-expired-jobs:false

# 每一个归档目录下可以保留的最大job数,设置为-1即不限制
historyserver.archive.retained-jobs:-1

# HistoryServer 地址
historyserver.web.address:0.0.0.0

# HistoryServer web地址
historyserver.web.port:9999

# web端刷新间隔
historyserver.web.refresh-interval: 10000

# 配置已归档jm路径
jobmanager.archive.fs.dir: hdfs:///project/flink/history-server/

#historyserver监控归档路径,该路径要和jm配置的一样
historyserver.archive.fs.dir:hdfs:///project/flink/history-server/

# 每10s扫描一次归档路径
historyserver.archive.fs.refresh-interval: 10000
  1. 启动HistoryServer服务
# 注意:要先配置Hadoop环境变量
export HADOOP_CLASSPATH=`hadoop classpath`

# 启动服务
bin/historyserver.sh start
  1. 提交per-job作业,用于测试
bin/flink run -t yarn-per-job --detached ./examples/streaming/TopSpeedWindowing.jar  -qu yarn.queue  -p 1

flink用户名密码 flink server_实时大数据_03

  1. web端停用该作业
  2. 查看归档路径信息
  3. flink用户名密码 flink server_hive_04

  4. 打开localhost:9999界面即可查看已结束掉的作业
  5. flink用户名密码 flink server_hive_05

flink用户名密码 flink server_实时大数据_06

可以看到该historyServer将作业完整的执行计划以及任务信息完整的展示出来,通过该服务可以及时定位到作业异常情况

Rest API

除了上述的web界面可以供用户进行查询,同时也提供了对外接口方便开发者进行封装集成到私有开发平台中。目前已提供的接口如下,所有的请求格式样例均为http://hostname:9999/jobs(注意:这里的9999端口是笔者更改后的)。另尖括号中的值是变量,例如http://hostname:port/jobs/<jobid>/exceptions将必须以http://hostname:port/jobs/7684be6004e4e955c2a558a9bc463f65/exceptions请求.

  • /config
  • /jobs/overview
  • /jobs/<jobid>
  • /jobs/<jobid>/vertices
  • /jobs/<jobid>/config
  • /jobs/<jobid>/exceptions
  • /jobs/<jobid>/accumulators
  • /jobs/<jobid>/vertices/<vertexid>
  • /jobs/<jobid>/vertices/<vertexid>/subtasktimes
  • /jobs/<jobid>/vertices/<vertexid>/taskmanagers
  • /jobs/<jobid>/vertices/<vertexid>/accumulators
  • /jobs/<jobid>/vertices/<vertexid>/subtasks/accumulators
  • /jobs/<jobid>/vertices/<vertexid>/subtasks/<subtasknum>
  • /jobs/<jobid>/vertices/<vertexid>/subtasks/<subtasknum>/attempts/<attempt>
  • /jobs/<jobid>/vertices/<vertexid>/subtasks/<subtasknum>/attempts/<attempt>/accumulators
  • /jobs/<jobid>/plan

这里的接口返回示例不再给出,读者们有兴趣可自行调试。

源码分析

HistoryServer的源码部分相对比较简单清晰,同时这里也把如何看源码的经验分享给大家。

  1. 首先一开始我们是通过historyserver.sh脚本来启动HistoryServer服务,那么这里先看下脚本的功能(一般其他相关框架的源码入口也是从脚本开始着手的,对hadoop熟悉的读者应该比较清楚)
  2. flink用户名密码 flink server_hive_07

  3. 可以看到,当我们使用start参数来启动的时候,调用的是flink-daemon.sh脚本
  4. 通过第一个脚本发现,在调用flink-daemon.sh脚本的时候传入了historyserver参数
  5. flink用户名密码 flink server_大数据_08

  6. 直接查看HistoryServer类,找到该类时,首先第一步要看的就是类注释,先了解一下该类主要实现什么功能
    如截图可见,该类主要是HistoryServer的入口,进行web启动和提供对外接口,同时有一个main方法,那么就方便我们下手了。

flink用户名密码 flink server_flink用户名密码_09

  1. 主要逻辑
//1.参数解析
ParameterTool pt = ParameterTool.fromArgs(args);

//2.加载flink-conf.yaml文件
final Configuration flinkConfig = GlobalConfiguration.loadConfiguration(configDir);

FileSystem.initialize(flinkConfig, PluginUtils.createPluginManagerFromRootFolder(flinkConfig));

//3.启动HistoryServer
SecurityUtils.install(new SecurityConfiguration(flinkConfig));
SecurityUtils.getInstalledContext().runSecured(new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
     HistoryServer hs = new HistoryServer(flinkConfig);
     hs.run();
     return 0;
    }
   });
  1. 从主入口可以看到,主要是通过调用run方法来启动HistoryServer,而run方法再次调用了start方法
public void run() {
  start();
}

void start() throws IOException, InterruptedException {
  synchronized (startupShutdownLock) {
      //1.创建本地目录用于缓存,本地文件路径通过“java.io.tmpdir”参数来配置,文件以flink-web-history-开头
   Files.createDirectories(webDir.toPath());
   Router router = new Router();
   router.addGet("/:*", new HistoryServerStaticFileServerHandler(webDir));
   if (!webDir.exists() && !webDir.mkdirs()) {
    throw new IOException("Failed to create local directory " + webDir.getAbsoluteFile() + ".");
   }
      
      //2.将job信息写到归档目录下的json文件
   createDashboardConfigFile();
      
      //3.启动归档Fetcher任务用于扫描归档目录下的所有作业
   archiveFetcher.start();
      
      //4.启动web ui
   netty = new WebFrontendBootstrap(router, LOG, webDir, serverSSLFactory, webAddress, webPort, config);
  }
 }

通过以上简单的源码分析,可以大致梳理出HistoryServer的工作原理,即把指定的hdfs归档目录下的作业内容写入到本地的临时目录缓存,大多数是Json文件。然后启动Fetcher任务来定期扫描归档目录下是否有新的作业,然后刷新web ui进行显示。

本地缓存目录

flink用户名密码 flink server_hive_10

该截图即是本地的缓存目录,该目录下大多是js文件和html相关文件,同时还包含job相关的信息,被写到了json文件中

flink用户名密码 flink server_hive_11

flink用户名密码 flink server_hive_12