背景
在我们实际生产开发中,Flink作业通常以per-job的模式提交到yarn集群上运行。当作业结束或因异常退出后,此时无法从yarn web ui上查看具体的日志信息来定位异常问题;如果yarn端未开启日志聚合,yarn logs命令就无法使用了,那么我们也就无法定位问题了。
History Server简介
当相应的Flink Cluster集群down掉后,Flink提供了一个History Server服务可以查询已归档的job,同时也对外提供了接口供用户进行封装获取数据。默认情况下,该服务绑定在localhost:8082端口上,需要用户手动启动。当然也可以把该服务以独立的服务运行,接下来讲解详细配置
相关配置demo
- 下载Flink1.12客户端
wget https://mirror.bit.edu.cn/apache/flink/flink-1.12.0/flink-1.12.0-bin-scala_2.11.tgz
- 修改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
- 启动HistoryServer服务
# 注意:要先配置Hadoop环境变量
export HADOOP_CLASSPATH=`hadoop classpath`
# 启动服务
bin/historyserver.sh start
- 提交per-job作业,用于测试
bin/flink run -t yarn-per-job --detached ./examples/streaming/TopSpeedWindowing.jar -qu yarn.queue -p 1
- web端停用该作业
- 查看归档路径信息
- 打开localhost:9999界面即可查看已结束掉的作业
可以看到该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的源码部分相对比较简单清晰,同时这里也把如何看源码的经验分享给大家。
- 首先一开始我们是通过historyserver.sh脚本来启动HistoryServer服务,那么这里先看下脚本的功能(一般其他相关框架的源码入口也是从脚本开始着手的,对hadoop熟悉的读者应该比较清楚)
- 可以看到,当我们使用start参数来启动的时候,调用的是flink-daemon.sh脚本
- 通过第一个脚本发现,在调用flink-daemon.sh脚本的时候传入了historyserver参数
- 直接查看HistoryServer类,找到该类时,首先第一步要看的就是类注释,先了解一下该类主要实现什么功能
如截图可见,该类主要是HistoryServer的入口,进行web启动和提供对外接口,同时有一个main方法,那么就方便我们下手了。
- 主要逻辑
//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;
}
});
- 从主入口可以看到,主要是通过调用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进行显示。
本地缓存目录
该截图即是本地的缓存目录,该目录下大多是js文件和html相关文件,同时还包含job相关的信息,被写到了json文件中