0
# Angular Web Dashboard
1
2
Modern TypeScript dashboard providing real-time monitoring, job visualization, resource management, and developer tools for Apache Flink clusters. Built with Angular 18 and Ng-Zorro (Ant Design) components.
3
4
## Package Information
5
6
- **Framework**: Angular 18
7
- **Language**: TypeScript
8
- **UI Library**: Ng-Zorro (Ant Design)
9
- **Build Tool**: Angular CLI
10
- **Visualization**: D3.js, G2, Dagre for charts and graphs
11
12
## Core Imports
13
14
```typescript
15
// Angular core
16
import { Component, Injectable, OnInit } from '@angular/core';
17
import { Router, ActivatedRoute } from '@angular/router';
18
import { HttpClient } from '@angular/common/http';
19
20
// Ng-Zorro components
21
import { NzTableModule } from 'ng-zorro-antd/table';
22
import { NzCardModule } from 'ng-zorro-antd/card';
23
import { NzButtonModule } from 'ng-zorro-antd/button';
24
25
// Dashboard services
26
import { JobService } from './services/job.service';
27
import { TaskManagerService } from './services/task-manager.service';
28
import { StatusService } from './services/status.service';
29
```
30
31
## Basic Usage
32
33
```typescript
34
// Component setup
35
@Component({
36
selector: 'flink-job-list',
37
template: `
38
<nz-table [nzData]="jobs" [nzLoading]="loading">
39
<thead>
40
<tr>
41
<th>Job Name</th>
42
<th>Status</th>
43
<th>Start Time</th>
44
<th>Actions</th>
45
</tr>
46
</thead>
47
<tbody>
48
<tr *ngFor="let job of jobs">
49
<td>{{ job.name }}</td>
50
<td><flink-job-badge [status]="job.state"></flink-job-badge></td>
51
<td>{{ job['start-time'] | humanizeDate }}</td>
52
<td>
53
<button nz-button (click)="viewJob(job.jid)">View</button>
54
</td>
55
</tr>
56
</tbody>
57
</nz-table>
58
`
59
})
60
export class JobListComponent implements OnInit {
61
jobs: JobOverview[] = [];
62
loading = false;
63
64
constructor(private jobService: JobService, private router: Router) {}
65
66
ngOnInit() {
67
this.loadJobs();
68
}
69
70
loadJobs() {
71
this.loading = true;
72
this.jobService.getJobs().subscribe(jobs => {
73
this.jobs = jobs;
74
this.loading = false;
75
});
76
}
77
78
viewJob(jobId: string) {
79
this.router.navigate(['/job', jobId]);
80
}
81
}
82
```
83
84
## Architecture
85
86
The Angular dashboard follows a modular architecture with:
87
88
- **Standalone Components**: Modern Angular approach with individual component modules
89
- **Service Layer**: Injectable services for API communication and state management
90
- **Route-based Lazy Loading**: Feature modules loaded on demand for performance
91
- **Type-safe Models**: Complete TypeScript interfaces for all data structures
92
- **Reactive Programming**: RxJS observables for async data handling
93
94
## Application Structure
95
96
### Main Routes
97
98
```typescript
99
// Route configuration
100
export const routes: Routes = [
101
{ path: '', redirectTo: '/overview', pathMatch: 'full' },
102
{ path: 'overview', loadComponent: () => import('./pages/overview/overview.component') },
103
{ path: 'submit', loadComponent: () => import('./pages/submit/submit.component') },
104
{ path: 'job-manager', loadComponent: () => import('./pages/job-manager/job-manager.component') },
105
{ path: 'task-manager', loadComponent: () => import('./pages/task-manager/task-manager.component') },
106
{ path: 'job', loadChildren: () => import('./pages/job/job.routes') }
107
];
108
```
109
110
### Core Layout
111
112
```typescript { .api }
113
/**
114
* Root application component with navigation sidebar
115
*/
116
@Component({
117
selector: 'flink-root',
118
template: `
119
<nz-layout class="main-layout">
120
<nz-sider [nzCollapsed]="isCollapsed" [nzTrigger]="null">
121
<div class="sidebar-logo">
122
<img src="assets/flink-logo.svg" alt="Apache Flink">
123
</div>
124
<ul nz-menu nzMode="inline" [nzInlineCollapsed]="isCollapsed">
125
<li nz-menu-item routerLink="/overview">
126
<i nz-icon nzType="dashboard"></i>
127
<span>Overview</span>
128
</li>
129
<li nz-menu-item routerLink="/submit">
130
<i nz-icon nzType="upload"></i>
131
<span>Submit Job</span>
132
</li>
133
<!-- Additional menu items -->
134
</ul>
135
</nz-sider>
136
<nz-layout>
137
<nz-header>
138
<i nz-icon [nzType]="isCollapsed ? 'menu-unfold' : 'menu-fold'"
139
(click)="isCollapsed = !isCollapsed"></i>
140
</nz-header>
141
<nz-content>
142
<router-outlet></router-outlet>
143
</nz-content>
144
</nz-layout>
145
</nz-layout>
146
`
147
})
148
export class AppComponent {
149
isCollapsed = false;
150
}
151
```
152
153
## Services and API Communication
154
155
### Core Services
156
157
#### JobService
158
159
```typescript { .api }
160
/**
161
* Comprehensive job operations and monitoring service
162
*/
163
@Injectable({
164
providedIn: 'root'
165
})
166
export class JobService {
167
constructor(private http: HttpClient) {}
168
169
/**
170
* Get list of all jobs (running and completed)
171
*/
172
getJobs(): Observable<JobOverview[]>;
173
174
/**
175
* Get detailed information for a specific job
176
*/
177
getJobDetail(jobId: string): Observable<JobDetail>;
178
179
/**
180
* Get job configuration
181
*/
182
getJobConfig(jobId: string): Observable<JobConfig>;
183
184
/**
185
* Get job execution plan/graph
186
*/
187
getJobPlan(jobId: string): Observable<Plan>;
188
189
/**
190
* Get job metrics and performance data
191
*/
192
getJobMetrics(jobId: string): Observable<JobMetrics>;
193
194
/**
195
* Get job checkpoints information
196
*/
197
getJobCheckpoints(jobId: string): Observable<JobCheckpoint>;
198
199
/**
200
* Get job exceptions and error information
201
*/
202
getJobExceptions(jobId: string): Observable<JobException>;
203
204
/**
205
* Get backpressure information for job
206
*/
207
getJobBackpressure(jobId: string): Observable<JobBackpressure>;
208
209
/**
210
* Get flame graph data for performance analysis
211
*/
212
getJobFlameGraph(jobId: string, vertexId: string): Observable<JobFlameGraph>;
213
214
/**
215
* Cancel a running job
216
*/
217
cancelJob(jobId: string, mode?: 'cancel' | 'stop'): Observable<void>;
218
219
/**
220
* Trigger savepoint for job
221
*/
222
triggerSavepoint(jobId: string, targetDirectory?: string): Observable<SavepointInfo>;
223
}
224
```
225
226
#### TaskManagerService
227
228
```typescript { .api }
229
/**
230
* Task Manager monitoring and management service
231
*/
232
@Injectable({
233
providedIn: 'root'
234
})
235
export class TaskManagerService {
236
constructor(private http: HttpClient) {}
237
238
/**
239
* Get list of all task managers
240
*/
241
getTaskManagers(): Observable<TaskManager[]>;
242
243
/**
244
* Get detailed task manager information
245
*/
246
getTaskManagerDetail(taskManagerId: string): Observable<TaskManagerDetail>;
247
248
/**
249
* Get task manager metrics
250
*/
251
getTaskManagerMetrics(taskManagerId: string): Observable<TaskManagerMetrics>;
252
253
/**
254
* Get task manager logs
255
*/
256
getTaskManagerLogs(taskManagerId: string, logType: 'out' | 'log'): Observable<string>;
257
258
/**
259
* Get thread dump for task manager
260
*/
261
getTaskManagerThreadDump(taskManagerId: string): Observable<ThreadDump>;
262
263
/**
264
* Get profiling information
265
*/
266
getTaskManagerProfile(taskManagerId: string): Observable<ProfilingResult>;
267
}
268
```
269
270
#### OverviewService
271
272
```typescript { .api }
273
/**
274
* Cluster overview and statistics service
275
*/
276
@Injectable({
277
providedIn: 'root'
278
})
279
export class OverviewService {
280
constructor(private http: HttpClient) {}
281
282
/**
283
* Get cluster overview with job and resource statistics
284
*/
285
getOverview(): Observable<Overview>;
286
287
/**
288
* Get cluster configuration information
289
*/
290
getConfiguration(): Observable<Configuration>;
291
}
292
```
293
294
#### StatusService
295
296
```typescript { .api }
297
/**
298
* Application status, configuration, and error messaging service
299
*/
300
@Injectable({
301
providedIn: 'root'
302
})
303
export class StatusService {
304
constructor(private http: HttpClient) {}
305
306
/**
307
* Get application configuration and feature flags
308
*/
309
getConfig(): Observable<DashboardConfiguration>;
310
311
/**
312
* Handle and display error messages
313
*/
314
handleError(error: any): void;
315
316
/**
317
* Show success notification
318
*/
319
showSuccess(message: string): void;
320
321
/**
322
* Show error notification
323
*/
324
showError(message: string): void;
325
}
326
```
327
328
### Data Models and Interfaces
329
330
#### Job Models
331
332
```typescript { .api }
333
/**
334
* Job overview information for listing
335
*/
336
export interface JobOverview {
337
jid: string;
338
name: string;
339
state: JobState;
340
'start-time': number;
341
'end-time': number;
342
duration: number;
343
'last-modification': number;
344
tasks: TaskStatusCounts;
345
}
346
347
/**
348
* Detailed job information
349
*/
350
export interface JobDetail extends JobOverview {
351
vertices: JobVertex[];
352
'status-counts': TaskStatusCounts;
353
plan: Plan;
354
timestamps: JobTimestamps;
355
}
356
357
/**
358
* Job vertex information for execution graph
359
*/
360
export interface JobVertex {
361
id: string;
362
name: string;
363
parallelism: number;
364
status: JobVertexState;
365
'start-time': number;
366
'end-time': number;
367
duration: number;
368
tasks: TaskStatusCounts;
369
metrics: VertexMetrics;
370
}
371
372
/**
373
* Job execution plan/graph
374
*/
375
export interface Plan {
376
jid: string;
377
name: string;
378
nodes: PlanNode[];
379
edges: PlanEdge[];
380
}
381
382
/**
383
* Job configuration
384
*/
385
export interface JobConfig {
386
jid: string;
387
name: string;
388
'execution-config': ExecutionConfig;
389
}
390
```
391
392
#### Monitoring Models
393
394
```typescript { .api }
395
/**
396
* Job metrics and performance data
397
*/
398
export interface JobMetrics {
399
vertices: VertexMetrics[];
400
watermarks: WatermarkInfo[];
401
throughput: ThroughputInfo[];
402
}
403
404
/**
405
* Job checkpoint information
406
*/
407
export interface JobCheckpoint {
408
counts: CheckpointCounts;
409
summary: CheckpointSummary;
410
history: CheckpointStatistics[];
411
latest: {
412
completed: CheckpointStatistics | null;
413
savepoint: CheckpointStatistics | null;
414
failed: CheckpointStatistics | null;
415
restored: CheckpointStatistics | null;
416
};
417
}
418
419
/**
420
* Job backpressure monitoring data
421
*/
422
export interface JobBackpressure {
423
status: 'OK' | 'LOW' | 'HIGH';
424
'backpressure-level': 'OK' | 'LOW' | 'HIGH';
425
'end-timestamp': number;
426
subtasks: SubtaskBackpressure[];
427
}
428
429
/**
430
* Flame graph data for performance analysis
431
*/
432
export interface JobFlameGraph {
433
data: FlameGraphNode[];
434
endTime: number;
435
maxStackDepth: number;
436
}
437
```
438
439
#### Resource Models
440
441
```typescript { .api }
442
/**
443
* Task Manager information and status
444
*/
445
export interface TaskManager {
446
id: string;
447
path: string;
448
dataPort: number;
449
jmxPort: number;
450
timeSinceLastHeartbeat: number;
451
slotsNumber: number;
452
freeSlots: number;
453
totalResource: ResourceProfile;
454
freeResource: ResourceProfile;
455
hardware: HardwareDescription;
456
memoryConfiguration: MemoryConfiguration;
457
}
458
459
/**
460
* Cluster overview statistics
461
*/
462
export interface Overview {
463
taskmanagers: number;
464
'slots-total': number;
465
'slots-available': number;
466
'jobs-running': number;
467
'jobs-finished': number;
468
'jobs-cancelled': number;
469
'jobs-failed': number;
470
'flink-version': string;
471
'flink-commit': string;
472
}
473
```
474
475
## Major Dashboard Features
476
477
### 1. Cluster Overview Dashboard
478
479
Real-time cluster monitoring with key metrics and navigation.
480
481
```typescript { .api }
482
/**
483
* Overview dashboard component showing cluster statistics
484
*/
485
@Component({
486
selector: 'flink-overview',
487
template: `
488
<div class="overview-cards">
489
<nz-card nzTitle="Task Managers">
490
<nz-statistic [nzValue]="overview?.taskmanagers" nzSuffix="running"></nz-statistic>
491
</nz-card>
492
<nz-card nzTitle="Available Slots">
493
<nz-statistic
494
[nzValue]="overview?.['slots-available']"
495
[nzTotal]="overview?.['slots-total']"
496
nzSuffix="/ {{ overview?.['slots-total'] }}">
497
</nz-statistic>
498
</nz-card>
499
<nz-card nzTitle="Running Jobs">
500
<nz-statistic
501
[nzValue]="overview?.['jobs-running']"
502
[nzValueStyle]="{ color: '#52c41a' }">
503
</nz-statistic>
504
</nz-card>
505
</div>
506
507
<flink-job-list [jobs]="runningJobs"></flink-job-list>
508
`
509
})
510
export class OverviewComponent implements OnInit {
511
overview: Overview | null = null;
512
runningJobs: JobOverview[] = [];
513
514
constructor(
515
private overviewService: OverviewService,
516
private jobService: JobService
517
) {}
518
519
ngOnInit() {
520
this.loadOverview();
521
this.loadRunningJobs();
522
}
523
}
524
```
525
526
### 2. Job Visualization and Monitoring
527
528
Interactive job execution graph with detailed metrics and monitoring.
529
530
```typescript { .api }
531
/**
532
* Interactive job graph visualization component using D3/Dagre
533
*/
534
@Component({
535
selector: 'flink-dagre',
536
template: `
537
<div class="dagre-container" #dagreContainer>
538
<svg #dagreSvg></svg>
539
<div class="dagre-controls">
540
<button nz-button (click)="zoomIn()">Zoom In</button>
541
<button nz-button (click)="zoomOut()">Zoom Out</button>
542
<button nz-button (click)="resetZoom()">Reset</button>
543
</div>
544
</div>
545
`
546
})
547
export class DagreComponent implements OnInit, OnDestroy {
548
@Input() plan: Plan | null = null;
549
@Input() jobDetail: JobDetail | null = null;
550
551
private svg: d3.Selection<SVGElement, unknown, HTMLElement, any>;
552
private zoom: d3.ZoomBehavior<SVGElement, unknown>;
553
554
ngOnInit() {
555
this.initializeSvg();
556
this.setupZoom();
557
if (this.plan) {
558
this.renderGraph();
559
}
560
}
561
562
private renderGraph() {
563
// D3/Dagre graph rendering logic
564
const g = new dagre.graphlib.Graph();
565
g.setGraph({});
566
g.setDefaultEdgeLabel(() => ({}));
567
568
// Add nodes and edges from plan
569
this.plan?.nodes.forEach(node => {
570
g.setNode(node.id, {
571
label: node.description,
572
width: 200,
573
height: 50
574
});
575
});
576
577
this.plan?.edges.forEach(edge => {
578
g.setEdge(edge.source, edge.target);
579
});
580
581
dagre.layout(g);
582
this.drawGraph(g);
583
}
584
}
585
```
586
587
### 3. Performance Monitoring
588
589
Comprehensive performance analysis with flame graphs and metrics visualization.
590
591
```typescript { .api }
592
/**
593
* Flame graph component for performance analysis
594
*/
595
@Component({
596
selector: 'flink-flame-graph',
597
template: `
598
<div class="flame-graph-container" #flameGraphContainer>
599
<div class="flame-graph-controls">
600
<nz-select [(ngModel)]="selectedVertex" (ngModelChange)="loadFlameGraph()">
601
<nz-option *ngFor="let vertex of vertices"
602
[nzValue]="vertex.id"
603
[nzLabel]="vertex.name">
604
</nz-option>
605
</nz-select>
606
<button nz-button (click)="refreshFlameGraph()">Refresh</button>
607
</div>
608
<div #flameGraph class="flame-graph"></div>
609
</div>
610
`
611
})
612
export class FlameGraphComponent implements OnInit {
613
@Input() jobId: string = '';
614
@Input() vertices: JobVertex[] = [];
615
616
selectedVertex: string = '';
617
flameGraphData: JobFlameGraph | null = null;
618
619
constructor(private jobService: JobService) {}
620
621
ngOnInit() {
622
if (this.vertices.length > 0) {
623
this.selectedVertex = this.vertices[0].id;
624
this.loadFlameGraph();
625
}
626
}
627
628
loadFlameGraph() {
629
if (this.jobId && this.selectedVertex) {
630
this.jobService.getJobFlameGraph(this.jobId, this.selectedVertex)
631
.subscribe(data => {
632
this.flameGraphData = data;
633
this.renderFlameGraph();
634
});
635
}
636
}
637
638
private renderFlameGraph() {
639
// D3 flame graph rendering using d3-flame-graph library
640
const flameGraph = flamegraph()
641
.width(960)
642
.cellHeight(18)
643
.transitionDuration(750)
644
.minFrameSize(5)
645
.transitionEase(d3.easeCubic)
646
.sort(true)
647
.title('')
648
.onClick((d: any) => console.info('clicked:', d));
649
650
d3.select(this.flameGraphContainer.nativeElement)
651
.select('.flame-graph')
652
.datum(this.flameGraphData?.data)
653
.call(flameGraph);
654
}
655
}
656
```
657
658
### 4. Resource Management
659
660
Task Manager and Job Manager monitoring with detailed metrics and logs.
661
662
```typescript { .api }
663
/**
664
* Task Manager detail component with metrics and management
665
*/
666
@Component({
667
selector: 'flink-task-manager-detail',
668
template: `
669
<nz-card nzTitle="Task Manager Details">
670
<nz-descriptions nzBordered>
671
<nz-descriptions-item nzTitle="ID">{{ taskManager?.id }}</nz-descriptions-item>
672
<nz-descriptions-item nzTitle="Address">{{ taskManager?.path }}</nz-descriptions-item>
673
<nz-descriptions-item nzTitle="Slots">
674
{{ taskManager?.freeSlots }} / {{ taskManager?.slotsNumber }}
675
</nz-descriptions-item>
676
<nz-descriptions-item nzTitle="Memory">
677
{{ taskManager?.hardware?.managedMemory | humanizeBytes }}
678
</nz-descriptions-item>
679
</nz-descriptions>
680
</nz-card>
681
682
<nz-tabset>
683
<nz-tab nzTitle="Metrics">
684
<flink-task-manager-metrics [taskManagerId]="taskManagerId"></flink-task-manager-metrics>
685
</nz-tab>
686
<nz-tab nzTitle="Logs">
687
<flink-log-viewer
688
[logUrl]="getLogUrl('log')"
689
[taskManagerId]="taskManagerId">
690
</flink-log-viewer>
691
</nz-tab>
692
<nz-tab nzTitle="Thread Dump">
693
<flink-thread-dump [taskManagerId]="taskManagerId"></flink-thread-dump>
694
</nz-tab>
695
</nz-tabset>
696
`
697
})
698
export class TaskManagerDetailComponent implements OnInit {
699
@Input() taskManagerId: string = '';
700
701
taskManager: TaskManagerDetail | null = null;
702
703
constructor(private taskManagerService: TaskManagerService) {}
704
705
ngOnInit() {
706
this.loadTaskManagerDetail();
707
}
708
709
loadTaskManagerDetail() {
710
this.taskManagerService.getTaskManagerDetail(this.taskManagerId)
711
.subscribe(tm => this.taskManager = tm);
712
}
713
714
getLogUrl(type: 'log' | 'out'): string {
715
return `/taskmanagers/${this.taskManagerId}/logs/${type}`;
716
}
717
}
718
```
719
720
## Shared Utilities and Pipes
721
722
### Custom Pipes for Data Formatting
723
724
```typescript { .api }
725
/**
726
* Humanize date pipe for readable timestamps
727
*/
728
@Pipe({ name: 'humanizeDate' })
729
export class HumanizeDatePipe implements PipeTransform {
730
transform(value: number): string {
731
if (!value) return '-';
732
return new Date(value).toLocaleString();
733
}
734
}
735
736
/**
737
* Humanize duration pipe for time intervals
738
*/
739
@Pipe({ name: 'humanizeDuration' })
740
export class HumanizeDurationPipe implements PipeTransform {
741
transform(value: number): string {
742
if (!value) return '-';
743
const seconds = Math.floor(value / 1000);
744
const minutes = Math.floor(seconds / 60);
745
const hours = Math.floor(minutes / 60);
746
747
if (hours > 0) return `${hours}h ${minutes % 60}m`;
748
if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
749
return `${seconds}s`;
750
}
751
}
752
753
/**
754
* Humanize bytes pipe for memory and storage sizes
755
*/
756
@Pipe({ name: 'humanizeBytes' })
757
export class HumanizeBytesPipe implements PipeTransform {
758
transform(bytes: number): string {
759
if (!bytes) return '0 B';
760
const k = 1024;
761
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
762
const i = Math.floor(Math.log(bytes) / Math.log(k));
763
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
764
}
765
}
766
```
767
768
### Badge Components for Status Indicators
769
770
```typescript { .api }
771
/**
772
* Job status badge component
773
*/
774
@Component({
775
selector: 'flink-job-badge',
776
template: `
777
<nz-badge
778
[nzStatus]="getBadgeStatus()"
779
[nzText]="status">
780
</nz-badge>
781
`
782
})
783
export class JobBadgeComponent {
784
@Input() status: JobState = 'INITIALIZING';
785
786
getBadgeStatus(): 'success' | 'processing' | 'error' | 'warning' | 'default' {
787
switch (this.status) {
788
case 'RUNNING': return 'processing';
789
case 'FINISHED': return 'success';
790
case 'FAILED': return 'error';
791
case 'CANCELED': return 'warning';
792
default: return 'default';
793
}
794
}
795
}
796
```
797
798
The Angular dashboard provides a comprehensive, modern web interface for Apache Flink monitoring and management with real-time updates, interactive visualizations, and extensive developer tools for troubleshooting and performance analysis.