1412 lines
67 KiB
JavaScript
1412 lines
67 KiB
JavaScript
function timeAndFrom(field, fromNow, emptyText = '未停止', showSource = true) {
|
|
let tpl = "${IF(" + field + " === 0, '(" + emptyText + ")', CONCATENATE('<span class=\"font-bold\">'," + fromNow + ",'</span>'))}"
|
|
if (showSource) {
|
|
return {
|
|
align: 'center',
|
|
width: 80,
|
|
type: 'tooltip-wrapper',
|
|
content: "${DATETOSTR(DATE(" + field + "))}",
|
|
inline: true,
|
|
className: 'mr-2',
|
|
disabledOn: `\${${field} === 0}`,
|
|
body: {
|
|
type: 'tpl',
|
|
tpl: tpl,
|
|
}
|
|
}
|
|
} else {
|
|
return {
|
|
align: 'center',
|
|
width: 140,
|
|
type: 'tpl',
|
|
tpl: "${DATETOSTR(DATE(" + field + "))}",
|
|
}
|
|
}
|
|
}
|
|
|
|
function applicationLogDialog() {
|
|
return {
|
|
type: 'action',
|
|
level: 'link',
|
|
actionType: 'dialog',
|
|
dialog: {
|
|
title: '应用日志',
|
|
size: 'xl',
|
|
actions: [],
|
|
body: [
|
|
{
|
|
type: 'service',
|
|
api: {
|
|
method: 'GET',
|
|
url: '${base}/log/query_application_log',
|
|
data: {
|
|
application_id: '${id}',
|
|
}
|
|
},
|
|
body: [
|
|
{
|
|
disabled: true,
|
|
type: 'editor',
|
|
name: 'detail',
|
|
label: '应用日志',
|
|
size: 'xxl',
|
|
placeholder: '没有内容',
|
|
options: {
|
|
wordWrap: 'on',
|
|
}
|
|
}
|
|
]
|
|
},
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
function yarnQueueCrud(clusters = undefined, queueNames = undefined) {
|
|
return {
|
|
type: 'crud',
|
|
api: {
|
|
method: 'get',
|
|
url: '${base}/yarn/queue_list',
|
|
data: {
|
|
clusters: '${cluster|default:undefined}',
|
|
names: '${queueName|default:undefined}'
|
|
},
|
|
},
|
|
affixHeader: false,
|
|
defaultParams: {
|
|
...(clusters ? {cluster: clusters} : {}),
|
|
...(queueNames ? {queueName: queueNames} : {}),
|
|
},
|
|
interval: 10000,
|
|
syncLocation: false,
|
|
silentPolling: true,
|
|
headerToolbar: [
|
|
"reload",
|
|
],
|
|
columns: [
|
|
{
|
|
name: 'queueName',
|
|
label: '队列名称',
|
|
width: 130,
|
|
type: 'tooltip-wrapper',
|
|
body: '${TRUNCATE(queueName, 20)}',
|
|
content: '${queueName}',
|
|
},
|
|
{
|
|
label: '当前容量',
|
|
type: 'progress',
|
|
value: '${ROUND((absoluteUsedCapacity * 100 / absoluteMaxCapacity), 0)}',
|
|
stripe: true,
|
|
animate: true,
|
|
showLabel: false,
|
|
map: [
|
|
{
|
|
value: 30,
|
|
color: "#28a745"
|
|
}, {
|
|
value: 90,
|
|
color: "#007bff"
|
|
},
|
|
{
|
|
value: 100,
|
|
color: "#dc3545"
|
|
}
|
|
],
|
|
},
|
|
{
|
|
label: '进度',
|
|
width: 35,
|
|
align: 'center',
|
|
type: 'tpl',
|
|
tpl: '${ROUND((absoluteUsedCapacity * 100 / absoluteMaxCapacity), 0)}%',
|
|
},
|
|
{
|
|
type: "operation",
|
|
label: "操作",
|
|
width: 100,
|
|
fixed: 'right',
|
|
buttons: [
|
|
{
|
|
label: "详情",
|
|
type: "button",
|
|
level: "link",
|
|
tooltip: '查看队列详情',
|
|
visibleOn: '${!root}',
|
|
actionType: 'dialog',
|
|
dialog: {
|
|
closeOnEsc: true,
|
|
closeOnOutside: true,
|
|
size: 'md',
|
|
close: true,
|
|
title: '队列详情',
|
|
body: {
|
|
type: 'property',
|
|
items: [
|
|
{label: 'CPU', content: '${resourcesUsed.vCores}'},
|
|
// 有空看看这个值的单位
|
|
{label: '内存', content: '${resourcesUsed.memory}', span: 2},
|
|
{label: '容量(%)', content: '${capacity}'},
|
|
{label: '最大容量(%)', content: '${maxCapacity}'},
|
|
{label: '已用容量(%)', content: '${usedCapacity}'},
|
|
{label: '绝对容量(%)', content: '${absoluteCapacity}'},
|
|
{label: '绝对最大容量(%)', content: '${absoluteMaxCapacity}'},
|
|
{label: '绝对已用容量(%)', content: '${absoluteUsedCapacity}'},
|
|
{label: '应用数量', content: '${numApplications}', span: 3},
|
|
{label: '最大应用数量', content: '${maxApplications}'},
|
|
{label: '活跃应用数量', content: '${numActiveApplications}'},
|
|
{label: '等待应用数量', content: '${numPendingApplications}'},
|
|
{label: '容器数量', content: '${numContainers}', span: 3},
|
|
{label: '已分配容器数量', content: '${numApplications}'},
|
|
{label: '预留容器数量数量', content: '${numApplications}'},
|
|
{label: '等待容器数量', content: '${numApplications}'},
|
|
]
|
|
},
|
|
actions: []
|
|
}
|
|
},
|
|
{
|
|
visibleOn: "${webUrl}",
|
|
label: "管理页面",
|
|
type: "action",
|
|
level: "link",
|
|
tooltip: '打开管理页面',
|
|
actionType: 'url',
|
|
url: '${webUrl}',
|
|
blank: true,
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
function yarnCrudColumns() {
|
|
return [
|
|
{
|
|
name: 'id',
|
|
label: 'ID',
|
|
width: 250,
|
|
fixed: 'left',
|
|
...copyField('id')
|
|
},
|
|
{
|
|
name: 'name',
|
|
label: '名称',
|
|
className: 'nowrap',
|
|
...copyField('name')
|
|
},
|
|
{
|
|
name: 'cluster',
|
|
label: '集群',
|
|
width: 65,
|
|
align: 'center',
|
|
type: 'tpl',
|
|
tpl: '<span class="label label-info">${cluster}</span>'
|
|
},
|
|
{
|
|
label: '用户',
|
|
width: 80,
|
|
type: 'tooltip-wrapper',
|
|
body: '${TRUNCATE(user, 8)}',
|
|
content: '${user}',
|
|
align: 'center',
|
|
},
|
|
{
|
|
name: 'startedTime',
|
|
label: '启动时间',
|
|
...timeAndFrom('startedTime', 'startTimeFromNow'),
|
|
sortable: true,
|
|
canAccessSuperData: false,
|
|
},
|
|
{
|
|
name: 'finishedTime',
|
|
label: '停止时间',
|
|
...timeAndFrom('finishedTime', 'finishTimeFromNow'),
|
|
sortable: true,
|
|
align: 'center',
|
|
canAccessSuperData: false,
|
|
},
|
|
{
|
|
name: 'state',
|
|
label: '运行状态',
|
|
width: 70,
|
|
align: 'center',
|
|
type: 'mapping',
|
|
canAccessSuperData: false,
|
|
map: {
|
|
'NEW': '<span class=\'label bg-pink-300\'>创建中</span>',
|
|
'NEW_SAVING': '<span class=\'label bg-purple-300\'>已创建</span>',
|
|
'SUBMITTED': '<span class=\'label bg-indigo-300\'>已提交</span>',
|
|
'ACCEPTED': '<span class=\'label bg-cyan-300\'>调度中</span>',
|
|
'RUNNING': '<span class=\'label label-success\'>运行中</span>',
|
|
'FINISHED': '<span class=\'label label-default\'>已结束</span>',
|
|
'FAILED': '<span class=\'label label-danger\'>已失败</span>',
|
|
'KILLED': '<span class=\'label label-warning\'>被停止</span>',
|
|
'*': '<span class=\'label bg-gray-300\'>${state}</span>'
|
|
},
|
|
filterable: {
|
|
multiple: true,
|
|
options: [
|
|
{label: '创建中', value: 'NEW'},
|
|
{label: '已创建', value: 'NEW_SAVING'},
|
|
{label: '已提交', value: 'SUBMITTED'},
|
|
{label: '调度中', value: 'ACCEPTED'},
|
|
{label: '运行中', value: 'RUNNING'},
|
|
{label: '已结束', value: 'FINISHED'},
|
|
{label: '已失败', value: 'FAILED'},
|
|
{label: '被停止', value: 'KILLED'},
|
|
]
|
|
}
|
|
}, {
|
|
name: 'finalStatus',
|
|
label: '最终状态',
|
|
width: 70,
|
|
align: 'center',
|
|
type: 'mapping',
|
|
canAccessSuperData: false,
|
|
map: {
|
|
'UNDEFINED': '<span class=\'label label-info\'>运行</span>',
|
|
'SUCCEEDED': '<span class=\'label label-success\'>成功</span>',
|
|
'FAILED': '<span class=\'label label-danger\'>失败</span>',
|
|
'KILLED': '<span class=\'label label-warning\'>终止</span>',
|
|
'ENDED': '<span class=\'label label-default\'>结束</span>',
|
|
'*': '<span class=\'label bg-gray-300\'>${state}</span>'
|
|
},
|
|
filterable: {
|
|
multiple: true,
|
|
options: [
|
|
{label: '运行', value: 'UNDEFINED'},
|
|
{label: '成功', value: 'SUCCEEDED'},
|
|
{label: '失败', value: 'FAILED'},
|
|
{label: '终止', value: 'KILLED'},
|
|
{label: '结束', value: 'ENDED'},
|
|
]
|
|
}
|
|
},
|
|
{
|
|
type: "operation",
|
|
label: "操作",
|
|
width: 100,
|
|
fixed: 'right',
|
|
buttons: [
|
|
/*{
|
|
disabled: true,
|
|
label: "停止",
|
|
type: "button",
|
|
level: "link",
|
|
tooltip: '从队列中移除该任务',
|
|
actionType: 'dialog',
|
|
dialog: {
|
|
closeOnEsc: true,
|
|
closeOnOutside: true,
|
|
size: 'sm',
|
|
close: true,
|
|
title: '停止确认',
|
|
body: '确认停止任务: <br>${name}<br> ?',
|
|
actions: [
|
|
{
|
|
type: 'button',
|
|
label: '取消',
|
|
actionType: 'cancel'
|
|
},
|
|
{
|
|
type: 'button',
|
|
label: '确认移除',
|
|
level: 'danger',
|
|
actionType: 'ajax',
|
|
api: '',
|
|
primary: true,
|
|
message: {
|
|
success: '操作成功',
|
|
failed: '操作失败'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},*/
|
|
{
|
|
disabledOn: '${trackingUrl == null}',
|
|
disabledTip: '无应用页面',
|
|
label: "应用页面",
|
|
type: "action",
|
|
level: "link",
|
|
tooltip: '打开应用页面: ${trackingUrl}',
|
|
actionType: 'url',
|
|
url: '${trackingUrl}',
|
|
blank: true,
|
|
},
|
|
{
|
|
label: '日志',
|
|
...applicationLogDialog(),
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
|
|
function simpleYarnDialog(cluster, title, filterField) {
|
|
return {
|
|
title: title,
|
|
actions: [],
|
|
data: {
|
|
base: '${base}',
|
|
name: `\${${filterField}}`,
|
|
flinkJob: '${flinkJob}',
|
|
tableMeta: '${tableMeta}',
|
|
},
|
|
size: 'xl',
|
|
body: [
|
|
{
|
|
type: 'service',
|
|
api: {
|
|
method: 'get',
|
|
url: '${base}/yarn/job_current',
|
|
data: {
|
|
clusters: `${cluster}`,
|
|
name: '${name}',
|
|
}
|
|
},
|
|
silentPolling: false,
|
|
body: [
|
|
{
|
|
type: 'wrapper',
|
|
size: 'none',
|
|
visibleOn: '${hasCurrent}',
|
|
body: [
|
|
{
|
|
type: 'service',
|
|
api: {
|
|
method: 'get',
|
|
url: '${base}/flink/overview',
|
|
data: {
|
|
url: '${current.trackingUrl}'
|
|
}
|
|
},
|
|
|
|
silentPolling: false,
|
|
body: [
|
|
{
|
|
type: 'property',
|
|
title: 'Flink 基本信息',
|
|
column: 4,
|
|
items: [
|
|
{label: 'Flink 版本', content: '${detail.flinkVersion}'},
|
|
{label: 'Flink 小版本', content: '${detail.flinkCommit}', span: 3},
|
|
{label: '运行中', content: '${detail.jobsRunning}'},
|
|
{label: '已结束', content: '${detail.jobsFinished}'},
|
|
{label: '已失败', content: '${detail.jobsFailed}'},
|
|
{label: '被取消', content: '${detail.jobsCanceled}'},
|
|
{
|
|
label: 'Slot (可用/总数)',
|
|
content: '${detail.slotsAvailable}/${detail.slotsTotal}',
|
|
span: 4
|
|
},
|
|
]
|
|
},
|
|
]
|
|
},
|
|
{
|
|
type: 'service',
|
|
api: {
|
|
method: 'get',
|
|
url: '${base}/flink/jobs',
|
|
data: {
|
|
url: '${current.trackingUrl}',
|
|
schema: '${tableMeta.schema}',
|
|
table: "${tableMeta.table}",
|
|
mode: "${flinkJob.runMode}",
|
|
}
|
|
},
|
|
silentPolling: false,
|
|
body: [
|
|
{
|
|
type: 'table',
|
|
title: '任务详情',
|
|
source: '${items}',
|
|
affixHeader: false,
|
|
columns: [
|
|
{
|
|
name: 'name',
|
|
label: '名称',
|
|
width: 2000,
|
|
},
|
|
{
|
|
label: 'Checkpoint',
|
|
width: 60,
|
|
align: 'center',
|
|
fixed: 'right',
|
|
type: 'tpl',
|
|
tpl: "${IF(checkpointMetrics, checkpointMetrics.complete + '/' + checkpointMetrics.inProgress +'/' + checkpointMetrics.failed, '-')}",
|
|
},
|
|
{
|
|
name: 'metrics.readRecords',
|
|
label: '读记录数',
|
|
width: 60,
|
|
align: 'center',
|
|
fixed: 'right',
|
|
},
|
|
{
|
|
name: 'metrics.writeRecords',
|
|
label: '写记录数',
|
|
width: 60,
|
|
align: 'center',
|
|
fixed: 'right',
|
|
},
|
|
{
|
|
label: '操作',
|
|
width: 60,
|
|
align: 'center',
|
|
fixed: 'right',
|
|
type: 'wrapper',
|
|
size: 'none',
|
|
body: [
|
|
{
|
|
disabled: true,
|
|
type: 'button',
|
|
label: '详情',
|
|
level: 'link',
|
|
size: 'xs',
|
|
actionType: 'url',
|
|
blank: true,
|
|
url: '${page}',
|
|
}
|
|
],
|
|
},
|
|
]
|
|
}
|
|
]
|
|
},
|
|
]
|
|
},
|
|
{
|
|
type: 'tpl',
|
|
tpl: '没有正在运行的任务',
|
|
visibleOn: '${!hasCurrent}',
|
|
},
|
|
],
|
|
},
|
|
{type: 'divider'},
|
|
{
|
|
type: 'crud',
|
|
api: {
|
|
method: 'get',
|
|
url: `\${base}/yarn/job_list`,
|
|
data: {
|
|
clusters: `${cluster}`,
|
|
page: '${page|default:undefined}',
|
|
count: '${perPage|default:undefined}',
|
|
order: '${orderBy|default:undefined}',
|
|
direction: '${orderDir|default:undefined}',
|
|
filter_state: '${state|default:undefined}',
|
|
filter_final_status: '${finalStatus|default:undefined}',
|
|
search_id: '${id|default:undefined}',
|
|
search_name: '${name}',
|
|
precise: true,
|
|
}
|
|
},
|
|
affixHeader: false,
|
|
interval: 10000,
|
|
syncLocation: false,
|
|
silentPolling: true,
|
|
resizable: false,
|
|
perPage: 10,
|
|
headerToolbar: [
|
|
"reload",
|
|
{
|
|
type: 'pagination',
|
|
layout: ['pager', 'perPage'],
|
|
maxButtons: 5,
|
|
showPageInput: false,
|
|
}
|
|
],
|
|
footerToolbar: [],
|
|
columns: yarnCrudColumns(),
|
|
}
|
|
],
|
|
}
|
|
}
|
|
|
|
function copyField(field, tips = '复制', ignoreLength = 0) {
|
|
let tpl = ignoreLength === 0 ? `\${${field}}` : `\${TRUNCATE(${field}, ${ignoreLength})}`
|
|
return {
|
|
type: 'wrapper',
|
|
size: 'none',
|
|
body: [
|
|
{
|
|
type: 'tpl',
|
|
className: 'mr-1',
|
|
tpl: tpl,
|
|
},
|
|
{
|
|
type: 'action',
|
|
level: 'link',
|
|
label: '',
|
|
icon: 'fa fa-copy',
|
|
size: 'xs',
|
|
actionType: 'copy',
|
|
content: `\$${field}`,
|
|
tooltip: `${tips}`,
|
|
},
|
|
]
|
|
}
|
|
}
|
|
|
|
function flinkJobProperty(id, name, runMode) {
|
|
return {
|
|
type: 'property',
|
|
title: 'Flink Job 配置',
|
|
items: [
|
|
{label: 'ID', content: copyField(id, '复制 ID')},
|
|
{label: '任务名称', content: copyField(name, '复制任务名')},
|
|
{
|
|
label: '任务模式',
|
|
content: {
|
|
...mappingField(`${runMode}`, runModeMapping),
|
|
}
|
|
},
|
|
],
|
|
}
|
|
}
|
|
|
|
function runMetaProperty(runMode) {
|
|
return {
|
|
type: 'property',
|
|
title: `${runMode} 运行时信息`,
|
|
items: [
|
|
{label: '运行集群', content: `\${${runMode}Runtime.cluster}`},
|
|
{label: '运行主机', content: copyField(`${runMode}Runtime.host`)},
|
|
{label: '进程', content: copyField(`${runMode}Runtime.jvmPid`)},
|
|
{label: '任务 ID', content: copyField(`${runMode}Runtime.applicationId`), span: 2},
|
|
{label: 'Jar 版本', content: `\${${runMode}Runtime.executorVersion}`},
|
|
{label: '任务名称', content: `\${${runMode}Runtime.flinkJobName}`},
|
|
{label: '容器 ID', content: `\${${runMode}Runtime.containerId}`, span: 2},
|
|
{label: '容器路径', content: copyField(`${runMode}Runtime.containerPath`, undefined, 120), span: 3},
|
|
]
|
|
}
|
|
}
|
|
|
|
function flinkJobDialog() {
|
|
return {
|
|
title: 'Flink job 详情',
|
|
actions: [],
|
|
closeOnEsc: true,
|
|
closeOnOutside: true,
|
|
showCloseButton: false,
|
|
size: 'md',
|
|
body: [
|
|
flinkJobProperty('flinkJobId', 'flinkJob.name', 'flinkJob.runMode'),
|
|
{type: 'divider'},
|
|
{
|
|
type: 'action',
|
|
label: '打开同步详情',
|
|
actionType: 'dialog',
|
|
dialog: simpleYarnDialog('b5-sync', '同步详情', 'syncJobName')
|
|
},
|
|
{type: 'divider'},
|
|
{
|
|
type: 'service',
|
|
api: {
|
|
method: 'get',
|
|
url: '${base}/table/list_metas',
|
|
data: {
|
|
flink_job_id: '${flinkJobId}'
|
|
},
|
|
},
|
|
canAccessSuperData: true,
|
|
body: [
|
|
{
|
|
type: "table",
|
|
title: "包含 Hudi 同步表",
|
|
source: "${items}",
|
|
columns: [
|
|
{
|
|
label: '别名',
|
|
type: 'wrapper',
|
|
size: 'none',
|
|
body: [
|
|
{
|
|
type: 'action',
|
|
level: 'link',
|
|
label: '${tableMeta.alias}',
|
|
size: 'xs',
|
|
actionType: 'dialog',
|
|
tooltip: '查看详情',
|
|
dialog: tableMetaDialog(),
|
|
},
|
|
{
|
|
type: 'action',
|
|
level: 'link',
|
|
label: '',
|
|
icon: 'fa fa-copy',
|
|
size: 'xs',
|
|
actionType: 'copy',
|
|
content: '${tableMeta.alias}',
|
|
tooltip: '复制别名',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: '并行度',
|
|
name: 'tableMeta.hudi.writeTasks',
|
|
align: 'center',
|
|
},
|
|
{
|
|
label: 'Bucket 数量',
|
|
name: 'tableMeta.hudi.bucketIndexNumber',
|
|
align: 'center',
|
|
},
|
|
{
|
|
label: '写并行度',
|
|
name: 'tableMeta.hudi.writeTasks',
|
|
align: 'center',
|
|
},
|
|
{
|
|
label: '压缩并行度',
|
|
name: 'tableMeta.hudi.compactionTasks',
|
|
align: 'center',
|
|
},
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
function tableMetaDialog() {
|
|
return {
|
|
title: 'Table 详情',
|
|
actions: [],
|
|
closeOnEsc: true,
|
|
closeOnOutside: true,
|
|
showCloseButton: false,
|
|
size: 'lg',
|
|
body: [
|
|
{
|
|
type: 'wrapper',
|
|
size: 'none',
|
|
body: [
|
|
{
|
|
type: 'tpl',
|
|
className: 'block font-bold mb-2',
|
|
tpl: '常用操作',
|
|
},
|
|
{
|
|
type: 'button-group',
|
|
tiled: false,
|
|
buttons: [
|
|
{
|
|
disabledOn: '${!syncRunning}',
|
|
disabledTip: '同步未运行',
|
|
label: '同步情况',
|
|
type: 'button',
|
|
icon: 'fa fa-arrows-rotate',
|
|
actionType: 'dialog',
|
|
dialog: simpleYarnDialog('b5-sync', '同步详情', 'syncJobName')
|
|
},
|
|
{
|
|
disabledOn: '${!compactionRunning}',
|
|
disabledTip: '压缩未运行',
|
|
label: '压缩情况',
|
|
type: 'action',
|
|
icon: 'fa fa-minimize',
|
|
actionType: 'dialog',
|
|
dialog: simpleYarnDialog('b1', '压缩详情', 'compactionJobName')
|
|
},
|
|
{
|
|
type: 'button',
|
|
label: '时间线',
|
|
icon: 'fa fa-timeline',
|
|
actionType: 'dialog',
|
|
dialog: {
|
|
title: 'Hudi 表时间线',
|
|
actions: [],
|
|
size: 'lg',
|
|
body: {
|
|
type: 'crud',
|
|
api: {
|
|
method: 'get',
|
|
url: '${base}/hudi/timeline/list',
|
|
data: {
|
|
page: '${page|default:undefined}',
|
|
count: '${perPage|default:undefined}',
|
|
order: '${orderBy|default:undefined}',
|
|
direction: '${orderDir|default:undefined}',
|
|
flink_job_id: '${flinkJobId|default:undefined}',
|
|
alias: '${tableMeta.alias|default:undefined}',
|
|
filter_type: '${filter_type|default:undefined}',
|
|
filter_action: '${filter_action|default:undefined}',
|
|
filter_state: '${filter_state|default:undefined}',
|
|
},
|
|
defaultParams: {
|
|
filter_type: 'active',
|
|
},
|
|
},
|
|
syncLocation: false,
|
|
silentPolling: true,
|
|
stopAutoRefreshWhenModalIsOpen: true,
|
|
resizable: false,
|
|
perPage: 10,
|
|
headerToolbar: [
|
|
"reload",
|
|
{
|
|
type: 'pagination',
|
|
layout: ['pager', 'perPage'],
|
|
maxButtons: 5,
|
|
showPageInput: false,
|
|
},
|
|
],
|
|
footerToolbar: [
|
|
{
|
|
type: 'pagination',
|
|
layout: ['pager', 'perPage'],
|
|
maxButtons: 5,
|
|
showPageInput: false,
|
|
},
|
|
],
|
|
columns: [
|
|
{
|
|
name: 'timestamp',
|
|
label: '时间点',
|
|
width: 150,
|
|
sortable: true,
|
|
},
|
|
{
|
|
name: 'filter_action',
|
|
label: '类型',
|
|
width: 100,
|
|
...mappingField('action', hudiTimelineActionMapping),
|
|
filterable: filterableField(hudiTimelineActionMapping, false),
|
|
},
|
|
{
|
|
name: 'filter_state',
|
|
label: ' 状态',
|
|
width: 80,
|
|
align: 'center',
|
|
...mappingField('state', hudiTimelineStateMapping),
|
|
filterable: filterableField(hudiTimelineStateMapping, true),
|
|
},
|
|
{
|
|
name: 'fileName',
|
|
label: '文件名',
|
|
},
|
|
{
|
|
name: 'filter_type',
|
|
label: '来源',
|
|
width: 60,
|
|
align: 'center',
|
|
...mappingField('type', hudiTimelineTypeMapping),
|
|
filterable: filterableField(hudiTimelineTypeMapping, true),
|
|
},
|
|
],
|
|
}
|
|
}
|
|
},
|
|
{
|
|
type: 'button',
|
|
label: 'Pulsar 队列',
|
|
icon: 'fa fa-message',
|
|
actionType: 'dialog',
|
|
dialog: {
|
|
title: '队列详情',
|
|
actions: [],
|
|
size: 'lg',
|
|
body: {
|
|
type: 'service',
|
|
api: {
|
|
method: 'get',
|
|
url: '${base}/pulsar/topic',
|
|
data: {
|
|
pulsar_url: '${tableMeta.pulsarAddress|default:undefined}',
|
|
topic: '${tableMeta.topic|default:undefined}',
|
|
}
|
|
},
|
|
body: [
|
|
{
|
|
type: 'property',
|
|
title: '基本信息',
|
|
items: [
|
|
{label: 'Topic', content: copyField('detail.name'), span: 2},
|
|
{label: '最末位移', content: copyField('detail.lastMessageId')},
|
|
],
|
|
},
|
|
{type: 'divider'},
|
|
{
|
|
type: 'property',
|
|
title: '指标信息',
|
|
column: 4,
|
|
items: [
|
|
{label: '入队列消息速率', content: '${detail.state.messageRateIn}'},
|
|
{
|
|
label: '出队列消息速率',
|
|
content: '${detail.state.messageRateOut}'
|
|
},
|
|
{
|
|
label: '入队列消息吞吐量',
|
|
content: '${detail.state.messageThroughputIn}'
|
|
},
|
|
{
|
|
label: '出队列消息吞吐量',
|
|
content: '${detail.state.messageThroughputOut}'
|
|
},
|
|
{
|
|
label: '入队列消息数量',
|
|
content: '${detail.state.messageInCounter}'
|
|
},
|
|
{
|
|
label: '出队列消息数量',
|
|
content: '${detail.state.messageOutCounter}'
|
|
},
|
|
{
|
|
label: '入队列消息字节数',
|
|
content: '${detail.state.byteInCounter}'
|
|
},
|
|
{
|
|
label: '出队列消息字节数',
|
|
content: '${detail.state.byteOutCounter}'
|
|
},
|
|
{label: '存储消息大小', content: '${detail.state. storageSize}'},
|
|
{label: '积压消息大小', content: '${detail.state.backlogSize}'},
|
|
{
|
|
label: '平均消息大小',
|
|
content: '${detail.state.averageMessageSize}'
|
|
},
|
|
],
|
|
},
|
|
{type: 'divider'},
|
|
{
|
|
type: 'table',
|
|
title: '消费者们',
|
|
source: '${detail.subscriptionStateVOS}',
|
|
itemAction: {
|
|
type: 'action',
|
|
actionType: 'dialog',
|
|
dialog: {
|
|
title: '详情',
|
|
closeOnEsc: true,
|
|
closeOnOutside: true,
|
|
showCloseButton: false,
|
|
actions: [],
|
|
size: 'md',
|
|
body: [
|
|
{
|
|
type: 'property',
|
|
column: 1,
|
|
items: [
|
|
{
|
|
label: '在此订阅上传递的消息的总速率(msg/s)',
|
|
content: '${messageRateOut}'
|
|
},
|
|
{
|
|
label: '此订阅提供的总吞吐量(字节/秒)',
|
|
content: '${messageThroughputOut}'
|
|
},
|
|
{
|
|
label: '传送给消费者的总字节数(字节)',
|
|
content: '${bytesOutCounter}'
|
|
},
|
|
{
|
|
label: '传递给消费者的消息总数(msg)',
|
|
content: '${messageOutCounter}'
|
|
},
|
|
{
|
|
label: '此订阅上重新传递的消息的总速率(msg/s)',
|
|
content: '${messageRateRedeliver}'
|
|
},
|
|
{
|
|
label: '分块消息调度速率',
|
|
content: '${chunkedMessageRate}'
|
|
},
|
|
{
|
|
label: '积压的大小(字节)',
|
|
content: '${backlogSize}'
|
|
},
|
|
{
|
|
label: '积压中不包含延迟消息的消息数',
|
|
content: '${messageBacklogNoDelayed}'
|
|
},
|
|
{
|
|
label: '验证订阅是否由于达到未确认消息的阈值而被阻止',
|
|
content: '${blockedSubscriptionOnUnackedMessages}'
|
|
},
|
|
{
|
|
label: '当前正在跟踪的延迟消息数',
|
|
content: '${messageDelayed}'
|
|
},
|
|
{
|
|
label: '订阅的未确认消息数',
|
|
content: '${unackedMessages}'
|
|
},
|
|
{
|
|
label: '单个活跃消费者订阅处于活跃状态的消费者名称(故障转移、独占)',
|
|
content: '${activeConsumerName}'
|
|
},
|
|
{
|
|
label: '此订阅上过期的消息总速率(msg/s)',
|
|
content: '${messageRateExpired}'
|
|
},
|
|
{
|
|
label: '此订阅上过期的消息总数',
|
|
content: '${totalMessageExpired}'
|
|
},
|
|
{
|
|
label: '最后一条消息过期时间',
|
|
content: {
|
|
type: 'tpl',
|
|
tpl: '${lastExpireTimestamp|date:YYYY-MM-DD HH\\:mm\\:ss:x}',
|
|
}
|
|
},
|
|
{
|
|
label: '上次接收的消费流命令时间',
|
|
content: {
|
|
type: 'tpl',
|
|
tpl: '${lastConsumedFlowTimestamp|date:YYYY-MM-DD HH\\:mm\\:ss:x}',
|
|
}
|
|
},
|
|
{
|
|
label: '上次消费消息时间',
|
|
content: {
|
|
type: 'tpl',
|
|
tpl: '${lastConsumedTimestamp|date:YYYY-MM-DD HH\\:mm\\:ss:x}',
|
|
}
|
|
},
|
|
{
|
|
label: '上次确认消息时间',
|
|
content: {
|
|
type: 'tpl',
|
|
tpl: '${lastAckedTimestamp|date:YYYY-MM-DD HH\\:mm\\:ss:x}',
|
|
}
|
|
},
|
|
{
|
|
label: '此订阅是持久订阅还是临时订阅',
|
|
content: '${durable}'
|
|
},
|
|
{
|
|
label: '标记订阅状态在不同区域之间保持同步',
|
|
content: '${replicated}'
|
|
},
|
|
]
|
|
},
|
|
]
|
|
}
|
|
},
|
|
columns: [
|
|
{
|
|
name: 'name',
|
|
label: "订阅名称",
|
|
...copyField('name')
|
|
},
|
|
{
|
|
name: 'type',
|
|
label: '订阅类型',
|
|
fixed: 'right',
|
|
...mappingField('type', subscriptionTypeMapping)
|
|
},
|
|
{name: 'messageBacklog', label: '积压', fixed: 'right'},
|
|
]
|
|
},
|
|
{type: 'divider'},
|
|
{
|
|
type: 'table',
|
|
title: '生产者们',
|
|
source: '${detail.state.publishers}',
|
|
itemAction: {
|
|
type: 'action',
|
|
actionType: 'dialog',
|
|
dialog: {
|
|
title: '详情',
|
|
closeOnEsc: true,
|
|
closeOnOutside: true,
|
|
showCloseButton: false,
|
|
actions: [],
|
|
size: 'md',
|
|
body: [
|
|
{
|
|
type: 'property',
|
|
column: 1,
|
|
items: [
|
|
{
|
|
label: '发布消息速率(msg/s)',
|
|
content: '${messageRateIn}'
|
|
},
|
|
{
|
|
label: '发布消息吞吐量(字节/秒)',
|
|
content: '${messageThroughputIn}'
|
|
},
|
|
{
|
|
label: '消息平均大小(字节)',
|
|
content: '${averageMessageSize}'
|
|
},
|
|
{
|
|
label: '接收到的分块消息总数(msg)',
|
|
content: '${chunkedMessageRate}'
|
|
},
|
|
{label: '生产者地址', content: '${address}'},
|
|
{label: '客户端版本', content: '${clientVersion}'},
|
|
]
|
|
},
|
|
]
|
|
}
|
|
},
|
|
columns: [
|
|
{
|
|
name: 'producerId',
|
|
label: "ID",
|
|
width: 50,
|
|
},
|
|
{
|
|
name: 'producerName',
|
|
label: "名称",
|
|
...copyField('producerName'),
|
|
},
|
|
{
|
|
name: 'connectedSince',
|
|
label: "连接时间",
|
|
type: 'tpl',
|
|
tpl: '${connectedSince}'
|
|
},
|
|
{
|
|
name: 'accessMode',
|
|
label: '发布类型',
|
|
fixed: 'right',
|
|
...mappingField('accessMode', publishTypeMapping),
|
|
},
|
|
]
|
|
},
|
|
]
|
|
}
|
|
}
|
|
},
|
|
]
|
|
},
|
|
]
|
|
},
|
|
{type: 'divider', visibleOn: '${syncRuntime}'},
|
|
{
|
|
visibleOn: '${syncRuntime}',
|
|
...runMetaProperty('sync'),
|
|
},
|
|
{type: 'divider', visibleOn: '${compactionRuntime}'},
|
|
{
|
|
visibleOn: '${compactionRuntime}',
|
|
...runMetaProperty('compaction'),
|
|
},
|
|
{type: 'divider'},
|
|
flinkJobProperty('flinkJobId', 'flinkJob.name', 'flinkJob.runMode'),
|
|
{type: 'divider'},
|
|
{
|
|
type: 'property',
|
|
title: 'Table 配置',
|
|
items: [
|
|
{label: '别名', content: copyField('tableMeta.alias', '复制别名')},
|
|
{
|
|
label: '分片表',
|
|
content: '${IF(tableMeta.type === \'sharding\', \'是\', \'否\')}'
|
|
},
|
|
{label: '分区字段', content: copyField('tableMeta.partitionField', '复制分区字段')},
|
|
{label: '源端', content: copyField('tableMeta.source', '复制源端')},
|
|
{label: '源库名', content: copyField('tableMeta.schema', '复制库名')},
|
|
{label: '源表名', content: copyField('tableMeta.table', '复制表名')},
|
|
{label: '源表类型', content: '${tableMeta.sourceType}'},
|
|
{label: '优先级', content: '${tableMeta.priority}', span: 2},
|
|
{
|
|
label: '标签',
|
|
content: {
|
|
type: 'each',
|
|
source: '${SPLIT(tableMeta.tags, ",")}',
|
|
items: mappingField('item', tagsMapping),
|
|
},
|
|
span: 3,
|
|
},
|
|
{label: '订阅 Topic', content: copyField('tableMeta.topic', '复制 Topic'), span: 3},
|
|
{
|
|
label: 'Pulsar 地址',
|
|
content: copyField('tableMeta.pulsarAddress', '复制地址', 130),
|
|
span: 3
|
|
},
|
|
{label: '过滤模式', content: mappingField('tableMeta.filterType', filterModeMapping)},
|
|
{label: '过滤字段', content: '${tableMeta.filterField}', span: 2},
|
|
{
|
|
label: '过滤内容',
|
|
content: {
|
|
type: 'each',
|
|
source: '${SPLIT(tableMeta.filterValues, ",")}',
|
|
items: {
|
|
type: 'tpl',
|
|
tpl: "<span class='label bg-info mr-1'>${item}</span>",
|
|
}
|
|
},
|
|
span: 3,
|
|
},
|
|
],
|
|
},
|
|
{type: 'divider'},
|
|
{
|
|
type: 'property',
|
|
title: 'Hudi 配置',
|
|
items: [
|
|
{label: '表类型', content: mappingField('tableMeta.hudi.targetTableType', hudiTableTypeMapping)},
|
|
{label: '库名', content: copyField('tableMeta.hudi.targetDataSource', '复制库名')},
|
|
{label: '表名', content: copyField('tableMeta.hudi.targetTable', '复制表名')},
|
|
{
|
|
label: 'HDFS',
|
|
content: copyField('tableMeta.hudi.targetHdfsPath', '复制路径'),
|
|
span: 3
|
|
},
|
|
{
|
|
label: 'Bucket 数量',
|
|
content: '${tableMeta.hudi.bucketIndexNumber}'
|
|
},
|
|
{
|
|
label: '保留文件版本',
|
|
content: '${tableMeta.hudi.keepFileVersion}'
|
|
},
|
|
{
|
|
label: '保留提交个数',
|
|
content: '${tableMeta.hudi.keepCommitVersion}'
|
|
},
|
|
{label: '写并行度', content: '${tableMeta.hudi.writeTasks}'},
|
|
{label: '读并行度', content: '${tableMeta.hudi.sourceTasks}'},
|
|
{
|
|
label: '压缩并行度',
|
|
content: '${tableMeta.hudi.compactionTasks}'
|
|
},
|
|
{
|
|
label: '写任务最大内存',
|
|
content: '${tableMeta.hudi. writeTaskMaxMemory}M'
|
|
},
|
|
{
|
|
label: '写批次大小',
|
|
content: '${tableMeta.hudi.writeBatchSize}M'
|
|
},
|
|
{
|
|
label: '写限速',
|
|
content: '${tableMeta.hudi.writeRateLimit}条/秒'
|
|
},
|
|
{
|
|
label: '压缩策略',
|
|
content: '${tableMeta.hudi. compactionStrategy}'
|
|
},
|
|
{
|
|
label: '压缩增量个数',
|
|
content: '${tableMeta.hudi.compactionDeltaCommits}'
|
|
},
|
|
{
|
|
label: '压缩增量时间',
|
|
content: '${tableMeta.hudi.compactionDeltaSeconds}秒'
|
|
},
|
|
],
|
|
},
|
|
{type: 'divider'},
|
|
{
|
|
type: 'property',
|
|
title: 'Yarn 配置',
|
|
column: 2,
|
|
items: [
|
|
{
|
|
label: '同步 JM 内存',
|
|
content: '${tableMeta.syncYarn.jobManagerMemory}M'
|
|
},
|
|
{
|
|
label: '同步 TM 内存',
|
|
content: '${tableMeta.syncYarn.taskManagerMemory}M'
|
|
},
|
|
{
|
|
label: '压缩 JM 内存',
|
|
content: '${tableMeta.compactionYarn.jobManagerMemory}M'
|
|
},
|
|
{
|
|
label: '压缩 TM 内存',
|
|
content: '${tableMeta.compactionYarn.taskManagerMemory}M'
|
|
},
|
|
],
|
|
},
|
|
{type: 'divider'},
|
|
{
|
|
type: 'property',
|
|
title: '其他配置',
|
|
column: 4,
|
|
items: [
|
|
{
|
|
label: '指标发布地址',
|
|
content: copyField('tableMeta.config.metricPublishUrl'),
|
|
span: 4
|
|
},
|
|
{
|
|
label: '指标打点延迟',
|
|
content: '${tableMeta.config.metricPublishDelay}'
|
|
},
|
|
{
|
|
label: '指标打点间隔',
|
|
content: '${tableMeta.config.metricPublishPeriod}'
|
|
},
|
|
{
|
|
label: '指标发布超时',
|
|
content: '${tableMeta.config.metricPublishTimeout}'
|
|
},
|
|
{
|
|
label: '指标批次数量',
|
|
content: '${tableMeta.config.metricPublishBatch}'
|
|
},
|
|
{
|
|
label: 'Prometheus 地址',
|
|
content: copyField('tableMeta.config.metricPrometheusUrl'),
|
|
span: 4
|
|
},
|
|
{
|
|
label: '事件提交服务地址',
|
|
content: copyField('tableMeta.config.metricApiUrl', '复制 URL', 130),
|
|
span: 4
|
|
},
|
|
{
|
|
label: 'Checkpoint 存储',
|
|
content: copyField('tableMeta.config.checkpointRootPath'),
|
|
span: 4
|
|
},
|
|
{
|
|
label: 'Zookeeper 地址',
|
|
content: copyField('tableMeta.config.zookeeperUrl'),
|
|
span: 4
|
|
},
|
|
],
|
|
},
|
|
{type: 'divider'},
|
|
{
|
|
type: 'table',
|
|
title: '表字段详情',
|
|
source: '${tableMeta.fields}',
|
|
resizable: false,
|
|
columns: [
|
|
{name: 'sequence', label: '排序', width: 50, align: 'center'},
|
|
{name: 'name', label: '字段名'},
|
|
{name: 'type', label: '字段类型', width: 100},
|
|
{name: 'length', label: '字段长度', width: 60, align: 'center'},
|
|
{
|
|
label: '是否主键',
|
|
align: 'center',
|
|
width: 50,
|
|
type: 'tpl',
|
|
tpl: "${primaryKey|isTrue:'<i class=\"fa fa-check-circle text-success text-md p-0\"></i>':'<i></i>'|raw}",
|
|
},
|
|
{
|
|
label: '是否分片键',
|
|
align: 'center',
|
|
width: 70,
|
|
type: 'tpl',
|
|
tpl: "${partitionKey|isTrue:'<i class=\"fa fa-check-circle text-success text-md p-0\"></i>':'<i></i>'|raw}",
|
|
}
|
|
],
|
|
}
|
|
],
|
|
}
|
|
}
|
|
|
|
function mappingItem(label, value, color = 'bg-info') {
|
|
return {
|
|
label: label,
|
|
value: value,
|
|
color: color,
|
|
}
|
|
}
|
|
|
|
let runModeMapping = [
|
|
mappingItem('1对1', 'ONE_IN_ONE', 'bg-pink-300'),
|
|
mappingItem('1对多', 'ALL_IN_ONE', 'bg-purple-300'),
|
|
mappingItem('按表1对多', 'ALL_IN_ONE_BY_TABLE', 'bg-cyan-300'),
|
|
mappingItem('按库1对多', 'ALL_IN_ONE_BY_SCHEMA', 'bg-indigo-300'),
|
|
]
|
|
|
|
let compactionStatusMapping = [
|
|
mappingItem('调度', 'SCHEDULE'),
|
|
mappingItem('开始', 'START', 'bg-primary'),
|
|
mappingItem('完成', 'FINISH', 'bg-success'),
|
|
mappingItem('失败', 'FAIL', 'bg-danger'),
|
|
]
|
|
|
|
let tagsMapping = [
|
|
mappingItem('不压缩', 'NO_COMPACT'),
|
|
mappingItem('备份Pulsar消息', 'PULSAR_BACKUP'),
|
|
mappingItem('无预合并', 'NO_PRE_COMBINE'),
|
|
mappingItem('不忽略写日志错误', 'NO_IGNORE_FAILED'),
|
|
mappingItem('取消算子合并', 'DISABLE_CHAINING'),
|
|
mappingItem('跟踪压缩op_ts', 'TRACE_LATEST_OP_TS'),
|
|
]
|
|
|
|
let hudiTableTypeMapping = [
|
|
mappingItem('MOR', 'MERGE_ON_READ'),
|
|
mappingItem('COW', 'COPY_ON_WRITE'),
|
|
]
|
|
|
|
let filterModeMapping = [
|
|
mappingItem('无', 'NONE', 'bg-pink-500'),
|
|
mappingItem('包含模式', 'INCLUDE', 'bg-purple-500'),
|
|
mappingItem('排除模式', 'EXCLUDE', 'bg-cyan-500'),
|
|
]
|
|
|
|
let subscriptionTypeMapping = [
|
|
mappingItem('独占', 'Exclusive', 'bg-pink-500'),
|
|
mappingItem('共享', 'Shared', 'bg-purple-500'),
|
|
mappingItem('灾备', 'Failover', 'bg-cyan-500'),
|
|
mappingItem('Key', 'Key_Shared', 'bg-green-500'),
|
|
]
|
|
|
|
let publishTypeMapping = [
|
|
mappingItem('共享', 'Shared', 'bg-pink-500'),
|
|
mappingItem('独占', 'Exclusive', 'bg-purple-500'),
|
|
mappingItem('等待独占', 'WaiteForExclusive', 'bg-cyan-500'),
|
|
]
|
|
|
|
let hudiTimelineActionMapping = [
|
|
mappingItem('Commit', 'commit'),
|
|
mappingItem('Delta Commit', 'deltacommit'),
|
|
mappingItem('Clean', 'clean', 'bg-cyan-500'),
|
|
mappingItem('Rollback', 'rollback', 'label-danger'),
|
|
mappingItem('Savepoint', 'savepoint'),
|
|
mappingItem('Replace Commit', 'replacecommit', 'label-warning'),
|
|
mappingItem('Compaction', 'compaction', 'bg-purple-500'),
|
|
mappingItem('Restore', 'restore', 'label-warning'),
|
|
mappingItem('Indexing', 'indexing'),
|
|
mappingItem('Schema Commit', 'schemacommit', 'label-warning'),
|
|
]
|
|
|
|
let hudiTimelineStateMapping = [
|
|
mappingItem('已提交', 'REQUESTED'),
|
|
mappingItem('操作中', 'INFLIGHT', 'label-warning'),
|
|
mappingItem('已完成', 'COMPLETED', 'label-success'),
|
|
mappingItem('错误', 'INVALID', 'label-danger'),
|
|
]
|
|
|
|
let hudiTimelineTypeMapping = [
|
|
mappingItem('活跃', 'active'),
|
|
mappingItem('归档', 'archive', 'bg-gray-300'),
|
|
]
|
|
|
|
let tableRunningStateMapping = [
|
|
mappingItem('运行中', 'true', 'label-success'),
|
|
mappingItem('未运行', 'false'),
|
|
]
|
|
|
|
function mappingField(field, mapping) {
|
|
let mapData = {
|
|
'*': `<span class='label bg-gray-300'>\${${field}}</span>`,
|
|
}
|
|
mapping.forEach(item => {
|
|
mapData[item['value']] = `<span class='label ${item['color']}'>${item['label']}</span>`
|
|
})
|
|
return {
|
|
type: 'mapping',
|
|
value: `\${${field}}`,
|
|
map: mapData,
|
|
}
|
|
}
|
|
|
|
function filterableField(mapping, multiple = false) {
|
|
return {
|
|
multiple: multiple,
|
|
options: [
|
|
...mapping
|
|
]
|
|
}
|
|
}
|