Unverified Commit 776473d7 authored by aiwenmo's avatar aiwenmo Committed by GitHub

[Feature-668][web] Add task manager info (#669)

* [Feature-654][web] Add task info tab

* [Feature-666][client] Capture column type conversion exception details in CDCSOURCE

* [Feature-668][web] Add task manager info
Co-authored-by: 's avatarwenmo <32723967+wenmo@users.noreply.github.com>
parent 84d7797e
...@@ -2,7 +2,9 @@ package com.dlink.controller; ...@@ -2,7 +2,9 @@ package com.dlink.controller;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
...@@ -13,9 +15,14 @@ import org.springframework.web.bind.annotation.RequestMapping; ...@@ -13,9 +15,14 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.dlink.api.FlinkAPI;
import com.dlink.assertion.Asserts;
import com.dlink.common.result.ProTableResult; import com.dlink.common.result.ProTableResult;
import com.dlink.common.result.Result; import com.dlink.common.result.Result;
import com.dlink.job.BuildConfiguration;
import com.dlink.model.JobInstance; import com.dlink.model.JobInstance;
import com.dlink.model.JobManagerConfiguration;
import com.dlink.model.TaskManagerConfiguration;
import com.dlink.service.JobInstanceService; import com.dlink.service.JobInstanceService;
import com.dlink.service.TaskService; import com.dlink.service.TaskService;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
...@@ -111,4 +118,31 @@ public class JobInstanceController { ...@@ -111,4 +118,31 @@ public class JobInstanceController {
public Result getLineage(@RequestParam Integer id) { public Result getLineage(@RequestParam Integer id) {
return Result.succeed(jobInstanceService.getLineage(id), "刷新成功"); return Result.succeed(jobInstanceService.getLineage(id), "刷新成功");
} }
/**
* 获取 JobManager 的信息
*/
@GetMapping("/getJobManagerInfo")
public Result getJobManagerInfo(@RequestParam String address) {
JobManagerConfiguration jobManagerConfiguration = new JobManagerConfiguration();
if (Asserts.isNotNullString(address)) {
FlinkAPI flinkAPI = FlinkAPI.build(address);
BuildConfiguration.buildJobManagerConfiguration(jobManagerConfiguration, flinkAPI);
}
return Result.succeed(jobManagerConfiguration, "获取成功");
}
/**
* 获取 TaskManager 的信息
*/
@GetMapping("/getTaskManagerInfo")
public Result getTaskManagerInfo(@RequestParam String address) {
Set<TaskManagerConfiguration> taskManagerConfigurationList = new HashSet<>();
if (Asserts.isNotNullString(address)) {
FlinkAPI flinkAPI = FlinkAPI.build(address);
JsonNode taskManagerContainers = flinkAPI.getTaskManagers();
BuildConfiguration.buildTaskManagerConfiguration(taskManagerConfigurationList, flinkAPI, taskManagerContainers);
}
return Result.succeed(taskManagerConfigurationList, "获取成功");
}
} }
...@@ -26,6 +26,8 @@ public interface JobInstanceService extends ISuperService<JobInstance> { ...@@ -26,6 +26,8 @@ public interface JobInstanceService extends ISuperService<JobInstance> {
JobInfoDetail getJobInfoDetailInfo(JobInstance jobInstance); JobInfoDetail getJobInfoDetailInfo(JobInstance jobInstance);
JobInfoDetail refreshJobInfoDetailInfo(JobInstance jobInstance);
LineageResult getLineage(Integer id); LineageResult getLineage(Integer id);
JobInstance getJobInstanceByTaskId(Integer id); JobInstance getJobInstanceByTaskId(Integer id);
......
package com.dlink.service.impl; package com.dlink.service.impl;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dlink.api.FlinkAPI;
import com.dlink.assertion.Asserts; import com.dlink.assertion.Asserts;
import com.dlink.common.result.ProTableResult; import com.dlink.common.result.ProTableResult;
import com.dlink.db.service.impl.SuperServiceImpl; import com.dlink.db.service.impl.SuperServiceImpl;
import com.dlink.db.util.ProTableUtil; import com.dlink.db.util.ProTableUtil;
import com.dlink.explainer.lineage.LineageBuilder; import com.dlink.explainer.lineage.LineageBuilder;
import com.dlink.explainer.lineage.LineageResult; import com.dlink.explainer.lineage.LineageResult;
import com.dlink.job.BuildConfiguration;
import com.dlink.job.FlinkJobTaskPool; import com.dlink.job.FlinkJobTaskPool;
import com.dlink.mapper.JobInstanceMapper; import com.dlink.mapper.JobInstanceMapper;
import com.dlink.model.*; import com.dlink.model.History;
import com.dlink.service.*; import com.dlink.model.JobInfoDetail;
import com.dlink.model.JobInstance;
import com.dlink.model.JobInstanceCount;
import com.dlink.model.JobInstanceStatus;
import com.dlink.model.JobStatus;
import com.dlink.service.ClusterConfigurationService;
import com.dlink.service.ClusterService;
import com.dlink.service.HistoryService;
import com.dlink.service.JobHistoryService;
import com.dlink.service.JobInstanceService;
import com.dlink.utils.JSONUtil; import com.dlink.utils.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* JobInstanceServiceImpl * JobInstanceServiceImpl
...@@ -110,6 +116,28 @@ public class JobInstanceServiceImpl extends SuperServiceImpl<JobInstanceMapper, ...@@ -110,6 +116,28 @@ public class JobInstanceServiceImpl extends SuperServiceImpl<JobInstanceMapper,
@Override @Override
public JobInfoDetail getJobInfoDetailInfo(JobInstance jobInstance) { public JobInfoDetail getJobInfoDetailInfo(JobInstance jobInstance) {
Asserts.checkNull(jobInstance, "该任务实例不存在");
String key = jobInstance.getId().toString();
FlinkJobTaskPool pool = FlinkJobTaskPool.getInstance();
if (pool.exist(key)) {
return pool.get(key);
} else {
JobInfoDetail jobInfoDetail = new JobInfoDetail(jobInstance.getId());
jobInfoDetail.setInstance(jobInstance);
jobInfoDetail.setCluster(clusterService.getById(jobInstance.getClusterId()));
jobInfoDetail.setJobHistory(jobHistoryService.getJobHistory(jobInstance.getId()));
History history = historyService.getById(jobInstance.getHistoryId());
history.setConfig(JSONUtil.parseObject(history.getConfigJson()));
jobInfoDetail.setHistory(history);
if (Asserts.isNotNull(history.getClusterConfigurationId())) {
jobInfoDetail.setClusterConfiguration(clusterConfigurationService.getClusterConfigById(history.getClusterConfigurationId()));
}
return jobInfoDetail;
}
}
@Override
public JobInfoDetail refreshJobInfoDetailInfo(JobInstance jobInstance) {
Asserts.checkNull(jobInstance, "该任务实例不存在"); Asserts.checkNull(jobInstance, "该任务实例不存在");
JobInfoDetail jobInfoDetail; JobInfoDetail jobInfoDetail;
FlinkJobTaskPool pool = FlinkJobTaskPool.getInstance(); FlinkJobTaskPool pool = FlinkJobTaskPool.getInstance();
...@@ -125,33 +153,15 @@ public class JobInstanceServiceImpl extends SuperServiceImpl<JobInstanceMapper, ...@@ -125,33 +153,15 @@ public class JobInstanceServiceImpl extends SuperServiceImpl<JobInstanceMapper,
if (Asserts.isNotNull(history) && Asserts.isNotNull(history.getClusterConfigurationId())) { if (Asserts.isNotNull(history) && Asserts.isNotNull(history.getClusterConfigurationId())) {
jobInfoDetail.setClusterConfiguration(clusterConfigurationService.getClusterConfigById(history.getClusterConfigurationId())); jobInfoDetail.setClusterConfiguration(clusterConfigurationService.getClusterConfigById(history.getClusterConfigurationId()));
} }
JobManagerConfiguration jobManagerConfiguration = new JobManagerConfiguration();
Set<TaskManagerConfiguration> taskManagerConfigurationList = new HashSet<>();
if (Asserts.isNotNullString(history.getJobManagerAddress()) && JobStatus.RUNNING.getValue().equals(jobInfoDetail.getInstance().getStatus())) { // 如果有jobManager地址,则使用该地址
FlinkAPI flinkAPI = FlinkAPI.build(history.getJobManagerAddress());
// 获取jobManager的配置信息 开始
BuildConfiguration.buildJobManagerConfiguration(jobManagerConfiguration, flinkAPI);
// 获取jobManager的配置信息 结束
// 获取taskManager的配置信息 开始
JsonNode taskManagerContainers = flinkAPI.getTaskManagers(); //获取taskManager列表
BuildConfiguration.buildTaskManagerConfiguration(taskManagerConfigurationList, flinkAPI, taskManagerContainers);
// 获取taskManager的配置信息 结束
}
jobInfoDetail.setJobManagerConfiguration(jobManagerConfiguration);
jobInfoDetail.setTaskManagerConfiguration(taskManagerConfigurationList);
if (pool.exist(key)) { if (pool.exist(key)) {
pool.refresh(jobInfoDetail);; pool.refresh(jobInfoDetail);
} else { } else {
pool.push(key,jobInfoDetail); pool.push(key, jobInfoDetail);
} }
return jobInfoDetail; return jobInfoDetail;
} }
@Override @Override
public LineageResult getLineage(Integer id) { public LineageResult getLineage(Integer id) {
History history = getJobInfoDetail(id).getHistory(); History history = getJobInfoDetail(id).getHistory();
......
package com.dlink.service.impl; package com.dlink.service.impl;
import cn.hutool.core.bean.BeanUtil; import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dlink.alert.*; import com.dlink.alert.Alert;
import com.dlink.alert.AlertConfig;
import com.dlink.alert.AlertMsg;
import com.dlink.alert.AlertResult;
import com.dlink.alert.ShowType;
import com.dlink.api.FlinkAPI; import com.dlink.api.FlinkAPI;
import com.dlink.assertion.Assert; import com.dlink.assertion.Assert;
import com.dlink.assertion.Asserts; import com.dlink.assertion.Asserts;
...@@ -24,31 +49,55 @@ import com.dlink.gateway.config.SavePointStrategy; ...@@ -24,31 +49,55 @@ import com.dlink.gateway.config.SavePointStrategy;
import com.dlink.gateway.config.SavePointType; import com.dlink.gateway.config.SavePointType;
import com.dlink.gateway.model.JobInfo; import com.dlink.gateway.model.JobInfo;
import com.dlink.gateway.result.SavePointResult; import com.dlink.gateway.result.SavePointResult;
import com.dlink.job.*; import com.dlink.job.BuildConfiguration;
import com.dlink.job.FlinkJobTask;
import com.dlink.job.FlinkJobTaskPool;
import com.dlink.job.Job;
import com.dlink.job.JobConfig;
import com.dlink.job.JobManager;
import com.dlink.job.JobResult;
import com.dlink.mapper.TaskMapper; import com.dlink.mapper.TaskMapper;
import com.dlink.metadata.driver.Driver; import com.dlink.metadata.driver.Driver;
import com.dlink.metadata.result.JdbcSelectResult; import com.dlink.metadata.result.JdbcSelectResult;
import com.dlink.model.*; import com.dlink.model.AlertGroup;
import com.dlink.model.AlertHistory;
import com.dlink.model.AlertInstance;
import com.dlink.model.Cluster;
import com.dlink.model.DataBase;
import com.dlink.model.History;
import com.dlink.model.Jar;
import com.dlink.model.JobHistory;
import com.dlink.model.JobInfoDetail;
import com.dlink.model.JobInstance;
import com.dlink.model.JobLifeCycle;
import com.dlink.model.JobManagerConfiguration;
import com.dlink.model.JobStatus;
import com.dlink.model.Savepoints;
import com.dlink.model.Statement;
import com.dlink.model.SystemConfiguration;
import com.dlink.model.Task;
import com.dlink.model.TaskManagerConfiguration;
import com.dlink.model.TaskVersion;
import com.dlink.result.SqlExplainResult; import com.dlink.result.SqlExplainResult;
import com.dlink.service.*; import com.dlink.service.AlertGroupService;
import com.dlink.service.AlertHistoryService;
import com.dlink.service.ClusterConfigurationService;
import com.dlink.service.ClusterService;
import com.dlink.service.DataBaseService;
import com.dlink.service.HistoryService;
import com.dlink.service.JarService;
import com.dlink.service.JobHistoryService;
import com.dlink.service.JobInstanceService;
import com.dlink.service.SavepointsService;
import com.dlink.service.StatementService;
import com.dlink.service.TaskService;
import com.dlink.service.TaskVersionService;
import com.dlink.utils.CustomStringJavaCompiler; import com.dlink.utils.CustomStringJavaCompiler;
import com.dlink.utils.JSONUtil; import com.dlink.utils.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource; import cn.hutool.core.bean.BeanUtil;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
/** /**
* 任务 服务实现类 * 任务 服务实现类
...@@ -721,7 +770,7 @@ public class TaskServiceImpl extends SuperServiceImpl<TaskMapper, Task> implemen ...@@ -721,7 +770,7 @@ public class TaskServiceImpl extends SuperServiceImpl<TaskMapper, Task> implemen
@Override @Override
public JobInfoDetail refreshJobInfoDetail(Integer id) { public JobInfoDetail refreshJobInfoDetail(Integer id) {
return jobInstanceService.getJobInfoDetailInfo(refreshJobInstance(id, true)); return jobInstanceService.refreshJobInfoDetailInfo(refreshJobInstance(id, true));
} }
@Override @Override
......
...@@ -58,6 +58,15 @@ const StudioTaskInfo = (props: any) => { ...@@ -58,6 +58,15 @@ const StudioTaskInfo = (props: any) => {
<Descriptions.Item label="方言"> <Descriptions.Item label="方言">
{current.task.dialect} {current.task.dialect}
</Descriptions.Item> </Descriptions.Item>
<Descriptions.Item label="版本">
{current.task.versionId}
</Descriptions.Item>
<Descriptions.Item label="创建于">
{current.task.createTime}
</Descriptions.Item>
<Descriptions.Item label="更新于">
{current.task.updateTime}
</Descriptions.Item>
</Descriptions> </Descriptions>
<Form <Form
form={form} form={form}
......
...@@ -76,3 +76,7 @@ div .ant-page-header { ...@@ -76,3 +76,7 @@ div .ant-page-header {
div .ant-pro-page-container-children-content{ div .ant-pro-page-container-children-content{
padding-top: 0px; padding-top: 0px;
} }
.ant-tabs-top > .ant-tabs-nav, .ant-tabs-bottom > .ant-tabs-nav, .ant-tabs-top > div > .ant-tabs-nav, .ant-tabs-bottom > div > .ant-tabs-nav {
margin: 0 0 4px 0!important;
}
import {Descriptions, Empty, Tabs} from 'antd';
import CodeShow from "@/components/Common/CodeShow";
const {TabPane} = Tabs;
const JobManagerConfiguration = (props: any) => {
const {job} = props;
const getMetricsConfigForm =() => {
let formList = [];
let tempData = job?.jobManagerConfiguration?.metrics;
for (let key in tempData) {
formList.push(
<Descriptions.Item label={key}>
{tempData[key]}
</Descriptions.Item>
)
}
return formList
}
const getJobManagerConfigForm = () => {
let formList = [];
let tempData = job?.jobManagerConfiguration?.jobManagerConfig;
for (let key in tempData) {
formList.push(
<Descriptions.Item label={key}>
{tempData[key]}
</Descriptions.Item>
)
}
return formList
}
return (
<>
<Tabs defaultActiveKey="metrics" size="small" tabPosition="top" style={{
border: "1px solid #f0f0f0",
}}>
<TabPane tab={<span>&nbsp; Metrics &nbsp;</span>} key="metrics">
<Descriptions bordered size="small" column={1}>
{getMetricsConfigForm()}
</Descriptions>
</TabPane>
<TabPane tab={<span>&nbsp; Configuration &nbsp;</span>} key="configuration">
<Descriptions bordered size="small" column={1}>
{getJobManagerConfigForm()}
</Descriptions>
</TabPane>
<TabPane tab={<span>&nbsp; Logs &nbsp;</span>} key="logs">
{(job?.jobManagerConfiguration?.jobManagerLog === ""|| job?.jobManagerConfig?.jobManagerLog === null) ?
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
: <CodeShow code={job?.jobManagerConfiguration?.jobManagerLog} language='java' height='500px'/>
}
</TabPane>
<TabPane tab={<span>&nbsp; Stdout &nbsp;</span>} key="stdout">
{(job?.jobManagerConfiguration?.jobManagerStdout === ""|| job?.jobManagerConfig?.jobManagerStdout === null) ?
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
: <CodeShow code={job?.jobManagerConfiguration?.jobManagerStdout} language='java' height='500px'/>
}
</TabPane>
</Tabs>
</>)
};
export default JobManagerConfiguration;
import {useState} from 'react';
import {PageContainer} from '@ant-design/pro-layout';
import ProCard from '@ant-design/pro-card';
import JobManagerConfiguration from "@/pages/DevOps/JobInfo/ClusterConfiguration/JobManager";
import TaskManagerConfigurationForm from "@/pages/DevOps/JobInfo/ClusterConfiguration/TaskManager";
import {JOB_STATUS} from "@/components/Common/JobStatus";
import {Empty} from "antd";
const ClusterConfiguration = (props: any) => {
const {job} = props;
const [tabKey, setTabKey] = useState<string>('jobmanager');
return (
<>
{job?.instance?.status === JOB_STATUS.RUNNING ?
<PageContainer
header={{title: undefined}}
tabList={[
{
tab: 'Job Manager',
key: 'jobmanager',
closable: false,
},
{
tab: 'Task Managers',
key: 'taskmanager',
closable: false,
},
]}
onTabChange={(key) => {
setTabKey(key);
}}
>
<ProCard>
{tabKey === 'jobmanager' ? <JobManagerConfiguration job={job}/> : undefined}
{tabKey === 'taskmanager' ? <TaskManagerConfigurationForm job={job}/> : undefined}
</ProCard>
</PageContainer> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
}
</>
);
};
export default ClusterConfiguration;
import {Descriptions, Empty, Tabs} from 'antd';
import CodeShow from "@/components/Common/CodeShow";
import {getJobManagerInfo} from "@/pages/DevOps/service";
import {useEffect, useState} from "react";
import {JobManagerConfiguration} from "@/pages/DevOps/data";
const {TabPane} = Tabs;
const JobManagerInfo = (props: any) => {
const {job} = props;
const [jobManager, setJobManager] = useState<JobManagerConfiguration>();
const refreshJobManagerInfo = () => {
const res = getJobManagerInfo(job?.history?.jobManagerAddress);
res.then((result) => {
setJobManager(result.datas);
});
}
useEffect(() => {
refreshJobManagerInfo();
}, []);
const getMetricsConfigForm = () => {
let formList = [];
let tempData = jobManager?.metrics;
for (let key in tempData) {
formList.push(
<Descriptions.Item label={key}>
{tempData[key]}
</Descriptions.Item>
)
}
return formList
}
const getJobManagerConfigForm = () => {
let formList = [];
let tempData = jobManager?.jobManagerConfig;
for (let key in tempData) {
formList.push(
<Descriptions.Item label={key}>
{tempData[key]}
</Descriptions.Item>
)
}
return formList
}
return (
<>
<Tabs defaultActiveKey="metrics" size="small" tabPosition="top" style={{
border: "1px solid #f0f0f0",
}}>
<TabPane tab={<span>&nbsp; Metrics &nbsp;</span>} key="metrics">
<Descriptions bordered size="small" column={1}>
{getMetricsConfigForm()}
</Descriptions>
</TabPane>
<TabPane tab={<span>&nbsp; Configuration &nbsp;</span>} key="configuration">
<Descriptions bordered size="small" column={1}>
{getJobManagerConfigForm()}
</Descriptions>
</TabPane>
<TabPane tab={<span>&nbsp; Logs &nbsp;</span>} key="logs">
{(jobManager?.jobManagerLog) ? <CodeShow code={jobManager?.jobManagerLog} language='java' height='500px'/>
: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
}
</TabPane>
<TabPane tab={<span>&nbsp; Stdout &nbsp;</span>} key="stdout">
{(jobManager?.jobManagerStdout) ?
<CodeShow code={jobManager?.jobManagerStdout} language='java' height='500px'/>
: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
}
</TabPane>
</Tabs>
</>)
};
export default JobManagerInfo;
...@@ -2,46 +2,36 @@ import {Button, Descriptions, Empty, Tabs} from 'antd'; ...@@ -2,46 +2,36 @@ import {Button, Descriptions, Empty, Tabs} from 'antd';
import CodeShow from "@/components/Common/CodeShow"; import CodeShow from "@/components/Common/CodeShow";
import ProTable, {ActionType, ProColumns} from "@ant-design/pro-table"; import ProTable, {ActionType, ProColumns} from "@ant-design/pro-table";
import {parseByteStr} from "@/components/Common/function"; import {parseByteStr} from "@/components/Common/function";
import {TaskManagerConfiguration} from "@/pages/DevOps/data"; import {TaskContainerConfigInfo, TaskManagerConfiguration} from "@/pages/DevOps/data";
import {useRef, useState} from "react"; import {useEffect, useRef, useState} from "react";
import {history} from "@@/core/history";
import {HomeOutlined} from "@ant-design/icons"; import {HomeOutlined} from "@ant-design/icons";
import {getTaskManagerInfo} from "@/pages/DevOps/service";
const {TabPane} = Tabs; const {TabPane} = Tabs;
const TaskManagerConfigurationForm = (props: any) => { const TaskManagerInfo = (props: any) => {
const {job} = props; const {job} = props;
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
const [isHistory, setIsHistory] = useState<boolean>(false); const [activeContainer, setActiveContainer] = useState<TaskContainerConfigInfo>();
const [taskManager, setTaskManager] = useState<TaskManagerConfiguration[]>();
const handleHistorySwicthChange = (checked: boolean) => { const refreshTaskManagerInfo = () => {
setIsHistory(checked); const res = getTaskManagerInfo(job?.history?.jobManagerAddress);
}; res.then((result) => {
setTaskManager(result.datas);
const taskManagerContainerListDataSource: TaskManagerConfiguration[] = [];
job?.taskManagerConfiguration?.forEach((entity: TaskManagerConfiguration) => {
taskManagerContainerListDataSource.push({
containerId: entity.containerId,
containerPath: entity.containerPath,
dataPort: entity.dataPort,
jmxPort: entity.jmxPort,
timeSinceLastHeartbeat: entity.timeSinceLastHeartbeat,
slotsNumber: entity.slotsNumber,
freeSlots: entity.freeSlots,
totalResource: entity.totalResource,
freeResource: entity.freeResource,
hardware: entity.hardware,
memoryConfiguration: entity.memoryConfiguration,
taskContainerConfigInfo: entity.taskContainerConfigInfo,
}); });
} }
);
useEffect(() => {
refreshTaskManagerInfo();
}, []);
const handleBack = () => { const handleBack = () => {
history.goBack(); setActiveContainer(undefined);
}; };
const getMetricsConfigForm =(metrics:any) => { const getMetricsConfigForm = (metrics: any) => {
let formList = []; let formList = [];
for (let key in metrics) { for (let key in metrics) {
formList.push( formList.push(
...@@ -53,12 +43,11 @@ const TaskManagerConfigurationForm = (props: any) => { ...@@ -53,12 +43,11 @@ const TaskManagerConfigurationForm = (props: any) => {
return formList return formList
} }
const buildContainerConfigInfo = () => {
const buildContainerConfigInfo =(entity: TaskManagerConfiguration) =>{
return ( return (
<> <>
<div style={{ marginBottom: 16 }}> <div style={{marginBottom: 4}}>
<Button title={'返回'} onClick={handleBack}> ← Back<HomeOutlined /> </Button> <Button title={'返回'} onClick={handleBack}> ← Back<HomeOutlined/> </Button>
</div> </div>
<Tabs defaultActiveKey="metrics" size="small" <Tabs defaultActiveKey="metrics" size="small"
...@@ -66,35 +55,33 @@ const TaskManagerConfigurationForm = (props: any) => { ...@@ -66,35 +55,33 @@ const TaskManagerConfigurationForm = (props: any) => {
border: "1px solid #f0f0f0", border: "1px solid #f0f0f0",
}}> }}>
<TabPane tab={<span>&nbsp; Metrics &nbsp;</span>} key="metrics"> <TabPane tab={<span>&nbsp; Metrics &nbsp;</span>} key="metrics">
{getMetricsConfigForm(entity?.taskContainerConfigInfo?.metrics)} <Descriptions bordered size="small" column={1}>
{getMetricsConfigForm(activeContainer?.metrics)}
</Descriptions>
</TabPane> </TabPane>
<TabPane tab={<span>&nbsp; Logs &nbsp;</span>} key="logs"> <TabPane tab={<span>&nbsp; Logs &nbsp;</span>} key="logs">
{(entity?.taskContainerConfigInfo?.taskManagerLog === "" || entity?.taskContainerConfigInfo?.taskManagerLog === null) ? {(activeContainer?.taskManagerLog) ?
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/> <CodeShow code={activeContainer?.taskManagerLog} language='java' height='500px'/>
: <CodeShow code={entity?.taskContainerConfigInfo?.taskManagerLog} language='java' height='500px'/> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
} }
</TabPane> </TabPane>
<TabPane tab={<span>&nbsp; Stdout &nbsp;</span>} key="stdout"> <TabPane tab={<span>&nbsp; Stdout &nbsp;</span>} key="stdout">
{(entity?.taskContainerConfigInfo?.taskManagerStdout === "" || entity?.taskContainerConfigInfo?.taskManagerLog === null) ? {(activeContainer?.taskManagerStdout) ?
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/> <CodeShow code={activeContainer?.taskManagerStdout} language='java' height='500px'/>
: <CodeShow code={entity?.taskContainerConfigInfo?.taskManagerStdout} language='java' height='500px'/> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
} }
</TabPane> </TabPane>
<TabPane tab={<span>&nbsp; Thread Dump &nbsp;</span>} key="threaddump"> <TabPane tab={<span>&nbsp; Thread Dump &nbsp;</span>} key="threaddump">
{(entity?.taskContainerConfigInfo?.taskManagerThreadDump === "" || entity?.taskContainerConfigInfo?.taskManagerThreadDump === null) ? {(activeContainer?.taskManagerThreadDump) ?
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/> <CodeShow code={activeContainer?.taskManagerThreadDump} language='java' height='500px'/>
: <CodeShow code={entity?.taskContainerConfigInfo?.taskManagerThreadDump} language='java' height='500px'/> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
} }
</TabPane> </TabPane>
</Tabs> </Tabs>
</> </>
) )
} }
// TODO: 点击[containerId]跳转到容器配置信息页面(buildContainerConfigInfo) 容器页面有返回按钮 可以返回到个列表页面
const columns: ProColumns<TaskManagerConfiguration>[] = [ const columns: ProColumns<TaskManagerConfiguration>[] = [
{ {
title: 'ID,Path', title: 'ID,Path',
...@@ -103,9 +90,10 @@ const TaskManagerConfigurationForm = (props: any) => { ...@@ -103,9 +90,10 @@ const TaskManagerConfigurationForm = (props: any) => {
render: (dom, entity) => { render: (dom, entity) => {
return ( return (
<> <>
<a style={{ width: 500 }} >{entity.containerId}</a> <a style={{width: 500}}
onClick={() => setActiveContainer(entity.taskContainerConfigInfo)}>{entity.containerId}</a>
<br/> <br/>
<span >{entity.containerPath}</span> <span>{entity.containerPath}</span>
</> </>
); );
}, },
...@@ -222,18 +210,14 @@ const TaskManagerConfigurationForm = (props: any) => { ...@@ -222,18 +210,14 @@ const TaskManagerConfigurationForm = (props: any) => {
}, },
]; ];
return ( return (
<> <>
{job?.taskManagerConfiguration?.length > 0 ? {activeContainer ? buildContainerConfigInfo() : undefined}
<ProTable<TaskManagerConfiguration> {(!activeContainer && taskManager?.length > 0) ?
(<ProTable<TaskManagerConfiguration>
columns={columns} columns={columns}
style={{width: '100%'}} style={{width: '100%'}}
dataSource={ taskManagerContainerListDataSource } dataSource={taskManager}
onDataSourceChange={(dataSource) => {
actionRef.current?.reload();
}}
actionRef={actionRef} actionRef={actionRef}
rowKey="containerId" rowKey="containerId"
pagination={{ pagination={{
...@@ -243,9 +227,11 @@ const TaskManagerConfigurationForm = (props: any) => { ...@@ -243,9 +227,11 @@ const TaskManagerConfigurationForm = (props: any) => {
dateFormatter="string" dateFormatter="string"
search={false} search={false}
size="small" size="small"
/>: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>} />)
: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
}
</> </>
) )
}; };
export default TaskManagerConfigurationForm; export default TaskManagerInfo;
import JobManagerInfo from "@/pages/DevOps/JobInfo/FlinkClusterInfo/JobManager";
import TaskManagerInfo from "@/pages/DevOps/JobInfo/FlinkClusterInfo/TaskManager";
import {Tabs} from "antd";
const {TabPane} = Tabs;
const FlinkClusterInfo = (props: any) => {
const {job} = props;
return (
<>
{
<Tabs defaultActiveKey="JobManagerInfo" size="small" tabPosition="top" style={{border: "1px solid #f0f0f0"}}>
<TabPane tab={<span>Job Manager</span>} key="JobManagerInfo">
<JobManagerInfo job={job}/>
</TabPane>
<TabPane tab={<span>Task Managers</span>} key="TaskManagerInfo">
<TaskManagerInfo job={job}/>
</TabPane>
</Tabs>
}
</>
);
};
export default FlinkClusterInfo;
...@@ -18,7 +18,8 @@ import FlinkSQL from "@/pages/DevOps/JobInfo/FlinkSQL"; ...@@ -18,7 +18,8 @@ import FlinkSQL from "@/pages/DevOps/JobInfo/FlinkSQL";
import Alert from "@/pages/DevOps/JobInfo/Alert"; import Alert from "@/pages/DevOps/JobInfo/Alert";
import DataMap from "@/pages/DevOps/JobInfo/DataMap"; import DataMap from "@/pages/DevOps/JobInfo/DataMap";
import CheckPoints from "@/pages/DevOps/JobInfo/CheckPoints"; import CheckPoints from "@/pages/DevOps/JobInfo/CheckPoints";
import ClusterConfiguration from "@/pages/DevOps/JobInfo/ClusterConfiguration"; import FlinkClusterInfo from "@/pages/DevOps/JobInfo/FlinkClusterInfo";
const {Link} = Typography; const {Link} = Typography;
...@@ -126,13 +127,10 @@ const JobInfo = (props: any) => { ...@@ -126,13 +127,10 @@ const JobInfo = (props: any) => {
<Button key="back" type="dashed" onClick={handleBack}>返回</Button>, <Button key="back" type="dashed" onClick={handleBack}>返回</Button>,
]; ];
buttons.push(<Button key="refresh" icon={<RedoOutlined/>} onClick={handleRefreshJobInfoDetail}/>); buttons.push(<Button key="refresh" icon={<RedoOutlined/>} onClick={handleRefreshJobInfoDetail}/>);
// if (job?.instance?.status as string === JOB_STATUS.RUNNING || job?.instance?.status as string ===JOB_STATUS.FAILED ||
// job?.instance?.status as string === JOB_STATUS.CANCELED || job?.instance?.status as string === JOB_STATUS.FINISHED) {
buttons.push(<Button key="flinkwebui"> buttons.push(<Button key="flinkwebui">
<Link href={`http://${job?.history?.jobManagerAddress}/#/job/${job?.instance?.jid}/overview`} target="_blank"> <Link href={`http://${job?.history?.jobManagerAddress}/#/job/${job?.instance?.jid}/overview`} target="_blank">
FlinkWebUI FlinkWebUI
</Link></Button>); </Link></Button>);
// }
buttons.push(<Button key="autorestart" type="primary" buttons.push(<Button key="autorestart" type="primary"
onClick={handleRestart}>重新{job?.instance?.step == 5 ? '上线' : '启动'}</Button>); onClick={handleRestart}>重新{job?.instance?.step == 5 ? '上线' : '启动'}</Button>);
if (!isStatusDone(job?.instance?.status as string)) { if (!isStatusDone(job?.instance?.status as string)) {
...@@ -259,7 +257,7 @@ const JobInfo = (props: any) => { ...@@ -259,7 +257,7 @@ const JobInfo = (props: any) => {
<ProCard> <ProCard>
{tabKey === 'base' ? <BaseInfo job={job}/> : undefined} {tabKey === 'base' ? <BaseInfo job={job}/> : undefined}
{tabKey === 'config' ? <Config job={job}/> : undefined} {tabKey === 'config' ? <Config job={job}/> : undefined}
{tabKey === 'cluster' ? <ClusterConfiguration job={job}/> : undefined} {tabKey === 'cluster' ? <FlinkClusterInfo job={job}/> : undefined}
{tabKey === 'snapshot' ? <CheckPoints job={job}/> : undefined} {tabKey === 'snapshot' ? <CheckPoints job={job}/> : undefined}
{tabKey === 'exception' ? <Exception job={job}/> : undefined} {tabKey === 'exception' ? <Exception job={job}/> : undefined}
{tabKey === 'log' ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/> : undefined} {tabKey === 'log' ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/> : undefined}
......
...@@ -47,8 +47,8 @@ export type JobInfoDetail = { ...@@ -47,8 +47,8 @@ export type JobInfoDetail = {
clusterConfiguration: ClusterConfigurationTableListItem, clusterConfiguration: ClusterConfigurationTableListItem,
history: HistoryItem, history: HistoryItem,
jobHistory: JobHistoryItem, jobHistory: JobHistoryItem,
jobManagerConfiguration : JobManagerConfiguration jobManagerConfiguration: JobManagerConfiguration
taskManagerConfiguration : List<TaskManagerConfiguration> taskManagerConfiguration: List<TaskManagerConfiguration>
jar: JarTableListItem jar: JarTableListItem
} }
...@@ -63,8 +63,6 @@ export type VerticesTableListItem = { ...@@ -63,8 +63,6 @@ export type VerticesTableListItem = {
tasks: any, tasks: any,
} }
export type JobHistoryItem = { export type JobHistoryItem = {
id: number, id: number,
job: string, job: string,
...@@ -78,20 +76,17 @@ export type JobHistoryItem = { ...@@ -78,20 +76,17 @@ export type JobHistoryItem = {
updateTime: string, updateTime: string,
} }
export type JobManagerConfiguration = { export type JobManagerConfiguration = {
metrics: string , metrics: any,
jobManagerConfig: string, jobManagerConfig: any,
jobManagerLog : string, jobManagerLog: string,
jobManagerStdout: string, jobManagerStdout: string,
} }
export type TaskManagerConfiguration = { export type TaskManagerConfiguration = {
containerId: string , containerId: string,
containerPath: string, containerPath: string,
dataPort : number, dataPort: number,
jmxPort: number, jmxPort: number,
timeSinceLastHeartbeat: number, timeSinceLastHeartbeat: number,
slotsNumber: number, slotsNumber: number,
...@@ -103,25 +98,9 @@ export type TaskManagerConfiguration = { ...@@ -103,25 +98,9 @@ export type TaskManagerConfiguration = {
taskContainerConfigInfo: TaskContainerConfigInfo, taskContainerConfigInfo: TaskContainerConfigInfo,
} }
export type TaskContainerConfigInfo = { export type TaskContainerConfigInfo = {
metrics: string , metrics: any,
taskManagerLog: string , taskManagerLog: string,
taskManagerStdout : string, taskManagerStdout: string,
taskManagerThreadDump: string, taskManagerThreadDump: string,
} }
export type CheckPointsDetailInfo = {
jobID: number,
historyID: number,
id: number,
status: string,
end_to_end_duration: number,
external_path : string,
latest_ack_timestamp: number,
state_size: number,
trigger_timestamp: number,
}
...@@ -5,13 +5,21 @@ export function getStatusCount() { ...@@ -5,13 +5,21 @@ export function getStatusCount() {
} }
export function getJobInfoDetail(id: number) { export function getJobInfoDetail(id: number) {
return getData("api/jobInstance/getJobInfoDetail",{id}); return getData("api/jobInstance/getJobInfoDetail", {id});
} }
export function refreshJobInfoDetail(id: number) { export function refreshJobInfoDetail(id: number) {
return getData("api/jobInstance/refreshJobInfoDetail",{id}); return getData("api/jobInstance/refreshJobInfoDetail", {id});
} }
export function getLineage(id: number) { export function getLineage(id: number) {
return getData("api/jobInstance/getLineage",{id}); return getData("api/jobInstance/getLineage", {id});
}
export function getJobManagerInfo(address: string) {
return getData("api/jobInstance/getJobManagerInfo", {address});
}
export function getTaskManagerInfo(address: string) {
return getData("api/jobInstance/getTaskManagerInfo", {address});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment