DataWorks提供了豐富的OpenAPI,您可以根據需要使用DataWorks的OpenAPI等開放能力實現各種業務場景。本文以使用運維中心的OpenAPI與OpenEvent為例,為您示例如何搭建運維大屏。
背景信息
本實踐搭建的運維大屏可滿足以下業務場景的需求:
- 實時任務的運行監控與節點狀態監控。
- 非實時任務的一周調度任務數量趨勢、任務完成情況、近一個月運行時長排行、近一個月任務出錯排行、昨日任務類型分布、昨日運行狀態分布等能力。
在進行本實踐之前,建議您先參考以下鏈接,了解DataWorks的開發平臺基本能力和概念:
準備工作:開啟并配置消息訂閱(OpenEvent)
在進行代碼開發前,您需要先在開放平臺中創建一個開放事件(OpenEvent),操作步驟請參見開啟并配置消息訂閱(OpenEvent)。
實踐1:實時任務運行監控
完成準備工作后,以下步驟將介紹如何使用開發平臺來實現實時任務的運行監控。
- 后端代碼開發。
- 前端代碼開發。前端部分使用儀表盤來表現,將后端接口返回的instanceSuccessCount與instanceFailureCount進行運算獲得任務的運行成功率。示例代碼如下。
import React from 'react'; import cn from 'classnames'; import { Loading } from '@alifd/next'; import { Gauge } from '@antv/g2plot'; import * as helpers from '../helpers'; import * as services from '../services'; import classes from '../styles/app.module.css'; export interface Props {} // 劃分刻度,50%以下表示劣,50%-90%為中,90%以上為優 const ticks = [0, 1 / 2, 9 / 10, 1]; const color = ['#F4664A', '#FAAD14', '#30BF78']; const RealtimeTaskMonitor: React.FunctionComponent<Props> = () => { const ref = React.useRef<HTMLDivElement>(null); const [percent, setPercent] = React.useState<number | undefined>(undefined); const [visible, setVisible] = React.useState(true); const [chart, setChart] = React.useState<Gauge>(); // 獲取后端數據 const fetchData = async () => { try { percent !== undefined && await helpers.pause(5000); const response = await services.workbench.getRealtimeNodeChanges(); // 計算任務運行成功率 let result = response.instanceSuccessCount / (response.instanceSuccessCount + response.instanceFailureCount); result = Number(result.toFixed(2)); setPercent(result); chart?.changeData(result); } catch (e) { console.error(e); } finally { visible && setVisible(false); } }; React.useEffect(() => { fetchData(); }, [ percent, chart, ]); React.useEffect(() => { if (ref.current) { const gauge = new Gauge(ref.current, { percent: percent || 0.5, range: { ticks, color: ['#F4664A', '#FAAD14', '#30BF78'], }, padding: 'auto', indicator: { pointer: { style: { stroke: '#D0D0D0', }, }, pin: { style: { stroke: '#D0D0D0', }, }, }, statistic: { title: { formatter: (datum) => { const nextPercent = datum?.percent; if (nextPercent < ticks[1]) { return `差(${Math.round(nextPercent * 100)}%)`; } if (nextPercent < ticks[2]) { return `中(${Math.round(nextPercent * 100)}%)`; } return `優(${Math.round(nextPercent * 100)}%)`; }, style: ({ percent }) => { return { fontSize: '36px', lineHeight: 1, color: percent < ticks[1] ? color[0] : percent < ticks[2] ? color[1] : color[2], }; }, }, content: { offsetY: 36, style: { fontSize: '24px', color: '#4B535E', }, formatter: () => '運行成功率', }, }, }); setChart(gauge); gauge.render(); return () => { gauge.destroy(); }; } }, [ ref.current, ]); return ( <Loading className={cn(classes.appLoading)} visible={visible}> <div ref={ref} /> </Loading> ); }; export default RealtimeTaskMonitor;
說明 上述代碼中使用pause函數,即暫停5秒,5秒后調用一次后端接口以獲取最新的任務運行狀態并刷新前端組件,然后再重新開始調用后端接口。 - 完成上述代碼開發后,您可在本地部署并運行工程代碼。部署并運行的操作請參見通用操作:本地部署。
實踐2:實時節點狀態監控
完成準備工作后,以下步驟將介紹如何使用開發平臺來實現實時節點狀態監控。
- 后端代碼開發。后端部分的實現過程與實時任務運行監控相同,調用的也是同一個接口,代碼示例請參見后端代碼開發。
- 前端代碼開發。前端部分使用了面積圖來展現已創建、已更新與已刪除三種節點狀態的分布情況。示例代碼如下。
import React from 'react'; import cn from 'classnames'; import moment from 'moment'; import { Loading } from '@alifd/next'; import { Area } from '@antv/g2plot'; import * as helpers from '../helpers'; import * as services from '../services'; import classes from '../styles/app.module.css'; export interface Props {} export interface Point { time: string; value: number; type: string; } const RealtimeNodeMonitor: React.FunctionComponent<Props> = () => { const ref = React.useRef(null); const [visible, setVisible] = React.useState(true); const [data, setData] = React.useState<Point[]>([]); const [chart, setChart] = React.useState<Area>(); const fetchData = async () => { try { data.length > 0 && await helpers.pause(5000); const response = await services.workbench.getRealtimeNodeChanges(); const time = moment().format('HH:mm:ss'); const nextData = data.concat([ { time, value: response.nodeChangeCreated, type: '已創建' }, { time, value: response.nodeChangeUpdated, type: '已更新' }, { time, value: response.nodeChangeDeleted, type: '已刪除' }, ]); setData(nextData); chart?.changeData(nextData); } catch (e) { console.error(e); } finally { visible && setVisible(false); } }; React.useEffect(() => { fetchData(); }, [ data, chart, ]); React.useEffect(() => { if (ref.current) { const area = new Area(ref.current, { data, padding: 'auto', xField: 'time', yField: 'value', seriesField: 'type', }); setChart(area); area.render(); return () => { area.destroy(); }; } }, [ ref.current, ]); return ( <Loading className={cn(classes.appLoading)} visible={visible}> <div ref={ref} /> </Loading> ); }; export default RealtimeNodeMonitor;
說明 上述代碼中使用pause函數,即暫停5秒,5秒后調用一次后端接口以獲取最新的任務運行狀態并刷新前端組件,然后再重新開始調用后端接口。 - 完成上述代碼開發后,您可在本地部署并運行工程代碼。部署并運行的操作請參見通用操作:本地部署。
實踐3:一周調度任務數量趨勢
完成準備工作后,以下步驟將介紹如何使用開發平臺來實現展示一周調度任務數量趨勢。
- 后端代碼開發。
- 前端代碼開發。在前端實現一個橫向的柱狀圖并請求后端接口進行渲染。
import React from 'react'; import cn from 'classnames'; import moment from 'moment'; import { Loading } from '@alifd/next'; import { Bar } from '@antv/g2plot'; import type { InstanceAmount } from '../services/workbench'; import * as services from '../services'; import classes from '../styles/app.module.css'; export interface Props { projectId: number; } const InstanceAmountList: React.FunctionComponent<Props> = (props) => { const ref = React.useRef(null); const [visible, setVisible] = React.useState(false); const [data, setData] = React.useState<InstanceAmount[]>([]); const [chart, setChart] = React.useState<Bar>(); // 獲取后端數據的方法 const fetchData = async (signal: AbortSignal, projectId: number) => { try { setVisible(true); // 開始時間使用7天前 const beginDate = moment().subtract(7, 'days').format('yyyy-MM-DDTHH:mm:ssZZ'); // 結束時間使用今天 const endDate = moment().format('yyyy-MM-DDTHH:mm:ssZZ'); const response = await services.workbench.getInstanceAmountList(signal, beginDate, endDate, projectId); chart?.changeData(response.map(i => ({ date: moment(i.date).format('YYYY-MM-DD'), count: i.count }))); setData(response); } catch (e) { console.error(e); } finally { setVisible(false); } }; // 開始渲染時獲取數據 React.useEffect(() => { const controller = new AbortController(); props.projectId && fetchData(controller.signal, props.projectId); return () => { controller.abort(); }; }, [ // 當projectId變化時重新獲取后端數據 props.projectId, ]); // 新建一個橫向的柱狀圖 React.useEffect(() => { if (ref.current) { const bar = new Bar(ref.current, { data, xField: 'count', yField: 'date', barBackground: { style: { fill: 'rgba(0,0,0,0.1)', }, }, height: 200, maxBarWidth: 10, }); bar.render(); setChart(bar); return () => { bar.destroy(); }; } }, [ ref.current, ]); // 渲染組件 return ( <Loading className={cn(classes.appLoading)} visible={visible}> <div ref={ref} /> </Loading> ); }; export default InstanceAmountList;
- 完成上述代碼開發后,您可在本地部署并運行工程代碼。部署并運行的操作請參見通用操作:本地部署。
實踐4:任務完成情況
完成準備工作后,以下步驟將介紹任務完成情況的整個實現過程。
- 后端代碼開發。
- 前端代碼開發。在前端實現一個折線圖組件來獲取并加載數據。
import React from 'react'; import cn from 'classnames'; import { Loading } from '@alifd/next'; import { Line } from '@antv/g2plot'; import * as services from '../services'; import type { SuccessInstanceAmount } from '../services/workbench'; import classes from '../styles/app.module.css'; export interface Props { projectId: number; } export interface Point extends SuccessInstanceAmount { type?: string; } const SuccessInstanceAmountList: React.FunctionComponent<Props> = (props) => { const ref = React.useRef(null); const [visible, setVisible] = React.useState(false); const [data, setData] = React.useState<Point[]>([]); const [chart, setChart] = React.useState<Line>(); // 獲取數據的方法 const fetchData = async (signal: AbortSignal, projectId: number) => { try { setVisible(true); const response = await services.workbench.getSuccessInstanceAmountList(signal, projectId); let collection: Point[] = []; // 把數據結構拍平以進行繪制 collection = collection.concat(response.avgTrend.map(i => ({ ...i, type: '平均值' }))); collection = collection.concat(response.todayTrend.map(i => ({ ...i, type: '今日' }))); collection = collection.concat(response.yesterdayTrend.map(i => ({ ...i, type: '昨日' }))); // 取最大值來美化Y軸的展示 const max = Math.max(...collection.map(i => i.count)) + 1; chart?.changeData(collection); chart?.update({ yAxis: { max } }); setData(collection); } catch (e) { console.error(e); } finally { setVisible(false); } }; // 當首次加載或projectId變化時,重新獲取后端數據并加載 React.useEffect(() => { const controller = new AbortController(); props.projectId && fetchData(controller.signal, props.projectId); return () => { controller.abort(); }; }, [ props.projectId, ]); // 構建折線圖 React.useEffect(() => { if (ref.current) { const line = new Line(ref.current, { data, xField: 'timePoint', yField: 'count', seriesField: 'type', height: 300, yAxis: { tickInterval: 1, }, }); setChart(line); line.render(); return () => { line.destroy(); }; } }, [ ref.current, ]); // 渲染組件 return ( <Loading className={cn(classes.appLoading)} visible={visible}> <div ref={ref} /> </Loading> ); }; export default SuccessInstanceAmountList;
- 完成上述代碼開發后,您可在本地部署并運行工程代碼。部署并運行的操作請參見通用操作:本地部署。
實踐5:近一個月運行時長排行
完成準備工作后,以下步驟將介紹近一個月運行時長排行的整個實現過程。
- 后端代碼開發。
- 前端代碼開發。在前端選擇表格組件來展示數據。
import React from 'react'; import moment from 'moment'; import { Table } from '@alifd/next'; import * as services from '../services'; import type { ConsumeTimeRank } from '../services/workbench'; export interface Props { projectId: number; } const { Column } = Table; const TopNInstanceElapsedTime: React.FunctionComponent<Props> = (props) => { const [visible, setVisible] = React.useState(false); const [dataSource, setDataSource] = React.useState<ConsumeTimeRank[]>([]); // 獲取后端數據的方法 const fetchData = async (signal: AbortSignal, projectId: number) => { try { setVisible(true); const response = await services.workbench.getTopNInstanceElapsedTime(signal, projectId); setDataSource(response.consumeTimeRank); } catch (e) { console.error(e); } finally { setVisible(false); } }; // 當初次渲染或projectId變化時,重新調用接口獲取數據并渲染 React.useEffect(() => { const controller = new AbortController(); props.projectId && fetchData(controller.signal, props.projectId); return () => { controller.abort(); }; }, [ props.projectId, ]); // 選染組件 return ( <Table loading={visible} dataSource={dataSource} maxBodyHeight={400} fixedHeader > <Column dataIndex="nodeId" title="節點ID" /> <Column dataIndex="nodeName" title="節點名稱" /> <Column dataIndex="instanceId" title="實例ID" /> <Column dataIndex="owner" title="負責人" /> <Column dataIndex="businessDate" cell={value => moment(value).format('YYYY-MM-DD')} title="業務日期" /> <Column dataIndex="consumed" title="運行時長" /> </Table> ); }; export default TopNInstanceElapsedTime;
- 完成上述代碼開發后,您可在本地部署并運行工程代碼。部署并運行的操作請參見通用操作:本地部署。
實踐6:近一個月任務出錯排行
完成準備工作后,以下步驟將介紹近一個月任務出錯排行的整個實現過程。
- 后端代碼開發。
- 前端代碼開發。在前端選擇表格組件來展示數據。
import React from 'react'; import { Table } from '@alifd/next'; import * as services from '../services'; import type { ErrorInstance } from '../services/workbench'; export interface Props { projectId: number; } const { Column } = Table; const TopNErrorInstance: React.FunctionComponent<Props> = (props) => { const [visible, setVisible] = React.useState(false); const [dataSource, setDataSource] = React.useState<ErrorInstance[]>([]); // 獲取數據的方法 const fetchData = async (signal: AbortSignal, projectId: number) => { try { setVisible(true); const response = await services.workbench.getTopNErrorInstance(signal, projectId); setDataSource(response.errorRank); } catch (e) { console.error(e); } finally { setVisible(false); } }; // 初次渲染或projectId變化時重新調用接口并更新渲染 React.useEffect(() => { const controller = new AbortController(); props.projectId && fetchData(controller.signal, props.projectId); return () => { controller.abort(); }; }, [ props.projectId, ]); // 泫染組件 return ( <Table loading={visible} dataSource={dataSource} maxBodyHeight={400} fixedHeader > <Column dataIndex="nodeId" title="節點ID" /> <Column dataIndex="nodeName" title="節點名稱" /> <Column dataIndex="owner" title="負責人" /> <Column dataIndex="programType" title="任務類型" cell={(value: number) => { switch (value) { case 6: return 'Shell'; case 10: return 'ODPS SQL'; case 11: return 'ODPS MR'; case 23: return '數據集成'; case 24: return 'ODPS Script'; case 99: return '虛擬節點'; case 221: return 'PyODPS 2'; case 225: return 'ODPS Spark'; case 227: return 'EMR Hive'; case 228: return 'EMR Spark'; case 229: return 'EMR Spark SQL'; case 230: return 'EMR MR'; case 239: return 'OSS對象檢查'; case 257: return 'EMR Shell'; case 258: return 'EMR Spark Shell'; case 259: return 'EMR Presto'; case 260: return 'EMR Impala'; case 900: return '實時同步'; case 1089: return '跨租戶節點'; case 1091: return 'Hologres開發'; case 1093: return 'Hologres SQL'; case 1100: return '賦值節點'; case 1221: return 'PyODPS 3'; } }} /> <Column dataIndex="count" title="出錯次數" /> </Table> ); }; export default TopNErrorInstance;
- 完成上述代碼開發后,您可在本地部署并運行工程代碼。部署并運行的操作請參見通用操作:本地部署。
實踐7:昨日任務類型分布
完成準備工作后,以下步驟將介紹昨日任務類型分布的整個實現過程。
- 后端代碼開發。
- 前端代碼開發。在前端選用餅圖來展示數據。
import React from 'react'; import { Loading } from '@alifd/next'; import { Pie } from '@antv/g2plot'; import cn from 'classnames'; import * as services from '../services'; import type { FileStatus } from '../services/workbench'; import classes from '../styles/app.module.css'; export interface Props { projectId: number; } const TaskStatus: React.FunctionComponent<Props> = (props) => { const ref = React.useRef(null); const [visible, setVisible] = React.useState(false); const [data, setData] = React.useState<FileStatus[]>([]); const [chart, setChart] = React.useState<Pie>(); // 獲取后端數據的方法 const fetchData = async (signal: AbortSignal, projectId: number) => { try { setVisible(true); const response = await services.workbench.getFileTypeStatistic(signal, projectId); chart?.changeData(response); setData(response); } catch (e) { console.error(e); } finally { setVisible(false); } }; // 首次渲染或projectId變化時重新獲取數據并渲染 React.useEffect(() => { const controller = new AbortController(); props.projectId && fetchData(controller.signal, props.projectId); return () => { controller.abort(); }; }, [ props.projectId, ]); // 實例化餅圖 React.useEffect(() => { if (ref.current) { const pie = new Pie(ref.current, { data, angleField: 'count', colorField: 'programType', label: { type: 'inner', offset: '-50%', content: '{value}', style: { textAlign: 'center', fontSize: 18, }, }, statistic: { title: false, }, }); setChart(pie); pie.render(); return () => { pie.destroy(); }; } }, [ ref.current, ]); // 渲染組件 return ( <Loading className={cn(classes.appLoading)} visible={visible}> <div ref={ref} /> </Loading> ); }; export default TaskStatus;
- 完成上述代碼開發后,您可在本地部署并運行工程代碼。部署并運行的操作請參見通用操作:本地部署。
實踐8:昨日運行狀態分布
完成準備工作后,以下步驟將介紹昨日運行狀態分布的整個實現過程。
- 后端代碼開發。
- 前端代碼開發。在前端選用餅圖來展示數據。
import React from 'react'; import { Loading } from '@alifd/next'; import { Pie } from '@antv/g2plot'; import cn from 'classnames'; import * as services from '../services'; import classes from '../styles/app.module.css'; export interface Props { projectId: number; } export interface Point { type: string; value: number; } const InstanceStatus: React.FunctionComponent<Props> = (props) => { const ref = React.useRef(null); const [visible, setVisible] = React.useState(false); const [data, setData] = React.useState<Point[]>([]); const [chart, setChart] = React.useState<Pie>(); // 獲取后端數據的方法 const fetchData = async (signal: AbortSignal) => { try { setVisible(true); const nextData: Point[] = []; const response = await services.workbench.getInstanceStatus(signal, props.projectId); nextData.push({ type: '未運行', value: response.notRunCount }); nextData.push({ type: '等待中', value: response.waitTimeCount + response.waitResCount }); nextData.push({ type: '運行中', value: response.runningCount }); nextData.push({ type: '運行成功', value: response.successCount }); nextData.push({ type: '運行失敗', value: response.failureCount }); chart?.changeData(nextData); setData(nextData); } catch (e) { console.error(e); } finally { setVisible(false); } }; // 首次渲染或projectId變化時重新獲取數據并渲染 React.useEffect(() => { const controller = new AbortController(); props.projectId && fetchData(controller.signal); return () => { controller.abort(); }; }, [ props.projectId, ]); // 實例化餅圖 React.useEffect(() => { if (ref.current) { const pie = new Pie(ref.current, { data, angleField: 'value', colorField: 'type', innerRadius: 0.6, label: { type: 'inner', offset: '-50%', content: '{value}', style: { textAlign: 'center', fontSize: 18, }, }, statistic: { title: false, }, }); setChart(pie); pie.render(); return () => { pie.destroy(); }; } }, [ ref.current, ]); // 渲染組件 return ( <Loading className={cn(classes.appLoading)} visible={visible}> <div ref={ref} /> </Loading> ); }; export default InstanceStatus;
- 完成上述代碼開發后,您可在本地部署并運行工程代碼。部署并運行的操作請參見通用操作:本地部署。
通用操作:本地部署
- 準備依賴環境。您需準備好以下依賴環境:java8及以上、maven構建工具、node環境、pnpm工具。您可以執行以下命令來確定是否具備上述環境:
npm -v // 如果已安裝成功,執行命令將展示版本號,否則會報沒有命令錯誤 java -version // 如果已安裝成功,執行命令將展示版本號,否則會報沒有命令錯誤 pnpm -v // 如果已安裝成功,執行命令將展示版本號,否則會報沒有命令錯誤
- 下載工程。工程下載鏈接:workbench-screen-demo.zip。下載工程后,進入工程中并執行以下命令:
pnpm i
- 在范例工程中的backend/src/main/resources路徑下找到application.properties文件,修改文件中的核心參數。
- api.access-key-id與api.access-key-secret為您阿里云賬號的AccessKey ID與AccessKey Secret。說明 您可以在AccessKey 管理頁面獲取阿里云賬號的相關信息。
- api.region-id、api.endpoint需修改為待調用OpenAPI的地域信息。
其他參數可根據實際情況修改,修改后的填寫示例如下。 - api.access-key-id與api.access-key-secret為您阿里云賬號的AccessKey ID與AccessKey Secret。
- 在工程根目錄下執行以下命令,運行示例實踐代碼。
npm run dev