SpringBoot 3.x + ECharts 5.4 动态大屏实战:3种数据源与5秒轮询刷新
·
SpringBoot 3.x + ECharts 5.4 动态大屏实战:3种数据源与5秒轮询刷新
在企业级数据监控场景中,动态数据展示已成为刚需。本文将手把手带你实现一个支持多数据源、具备实时刷新能力的可视化大屏系统。不同于基础教程,我们重点解决工程化落地中的三个核心问题: 异构数据源整合 、 前端高频轮询优化 和 大屏性能调优 。
1. 工程架构设计
1.1 技术栈选型
- 后端框架 :SpringBoot 3.1.5(JDK17+)
- 数据可视化 :ECharts 5.4.3
- 数据源支持 :
- MySQL 8.0(关系型数据库)
- 本地JSON文件(静态数据)
- REST API模拟(动态数据)
1.2 项目结构
src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── demo/
│ │ ├── config/ # 配置类
│ │ ├── controller/ # 数据接口
│ │ ├── service/ # 数据服务
│ │ └── Application.java
│ └── resources/
│ ├── static/ # 前端资源
│ │ ├── json/ # 静态JSON数据
│ │ └── js/ # ECharts库
│ └── templates/ # 大屏HTML
2. 多数据源集成方案
2.1 MySQL实时查询
采用MyBatis-Plus实现高效数据访问:
@Mapper
public interface MetricMapper extends BaseMapper<Metric> {
@Select("SELECT metric_name, value FROM realtime_metrics WHERE update_time > NOW() - INTERVAL 5 SECOND")
List<Metric> selectLatestMetrics();
}
性能优化点 :
- 添加
update_time索引 - 使用
@Cacheable缓存查询结果 - 限制返回字段避免
SELECT *
2.2 静态JSON文件
在 resources/static/json/ 放置数据文件:
// sales_data.json
{
"categories": ["Q1", "Q2", "Q3", "Q4"],
"values": [1250, 1890, 2100, 1800]
}
通过Spring MVC直接暴露接口:
@GetMapping("/api/json/sales")
public ResponseEntity<?> getSalesData() throws IOException {
Resource resource = new ClassPathResource("static/json/sales_data.json");
return ResponseEntity.ok().body(FileCopyUtils.copyToByteArray(resource.getInputStream()));
}
2.3 模拟API数据
使用随机数生成动态数据:
@RestController
@RequestMapping("/api/simulate")
public class SimulateController {
private final Random random = new Random();
@GetMapping("/traffic")
public Map<String, Object> getTrafficData() {
return Map.of(
"time", LocalTime.now().truncatedTo(ChronoUnit.SECONDS),
"inbound", random.nextInt(1000),
"outbound", random.nextInt(800)
);
}
}
3. 前端轮询实现
3.1 核心轮询逻辑
let refreshInterval = 5000; // 5秒
let chartInstances = []; // 存储图表实例
function startPolling() {
chartInstances.forEach(chart => {
setInterval(() => {
fetch(chart.dataUrl)
.then(res => res.json())
.then(data => {
chart.instance.setOption(updateChartOption(data));
chart.instance.resize(); // 自适应容器大小
});
}, refreshInterval);
});
}
3.2 避免内存泄漏
// 页面卸载时清理定时器
window.addEventListener('beforeunload', () => {
chartInstances.forEach(chart => {
clearInterval(chart.timer);
});
});
4. 大屏性能优化
4.1 ECharts配置建议
option = {
animation: false, // 关闭动画提升性能
dataset: {
dimensions: ['product', 'sales'],
source: []
},
series: [{
type: 'bar',
large: true, // 开启大数据优化
progressiveChunkMode: 'mod' // 分片加载
}]
};
4.2 服务端缓存策略
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return new CaffeineCacheManager("metrics") {
@Override
protected Cache<Object, Object> createNativeCache(String name) {
return Caffeine.newBuilder()
.expireAfterWrite(3, TimeUnit.SECONDS)
.maximumSize(1000)
.build();
}
};
}
}
5. 完整示例:电商大屏实战
5.1 后端控制器
@RestController
@RequestMapping("/api/dashboard")
public class DashboardController {
@Autowired
private MetricService metricService;
@GetMapping("/realtime")
@Cacheable(value = "metrics", key = "'realtime'")
public ResponseEntity<List<Metric>> getRealtimeMetrics() {
return ResponseEntity.ok(metricService.getLatestMetrics());
}
}
5.2 前端大屏实现
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>电商实时大屏</title>
<script src="/js/echarts.min.js"></script>
<style>
.dashboard-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
padding: 20px;
background: #0f1c3c;
}
.chart-panel {
height: 400px;
background: #1a2b50;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="dashboard-container">
<div id="sales-chart" class="chart-panel"></div>
<div id="user-chart" class="chart-panel"></div>
<div id="traffic-chart" class="chart-panel"></div>
</div>
<script>
// 初始化所有图表
function initCharts() {
const charts = [
{ id: 'sales-chart', url: '/api/dashboard/realtime', type: 'line' },
{ id: 'user-chart', url: '/api/json/user_metrics', type: 'pie' },
{ id: 'traffic-chart', url: '/api/simulate/traffic', type: 'bar' }
];
charts.forEach(config => {
const chart = echarts.init(document.getElementById(config.id));
chart.setOption(getBaseOption(config.type));
chartInstances.push({
instance: chart,
dataUrl: config.url
});
});
startPolling();
}
// 页面加载完成后初始化
window.onload = initCharts;
</script>
</body>
</html>
6. 异常处理与监控
6.1 服务端全局异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(DataAccessException.class)
public ResponseEntity<Map<String, Object>> handleDBError() {
return ResponseEntity.status(503)
.body(Map.of(
"timestamp", Instant.now(),
"status", 503,
"error", "Service Unavailable",
"message", "数据库服务不可用"
));
}
}
6.2 前端错误重试机制
function fetchWithRetry(url, retries = 3) {
return fetch(url)
.catch(err => {
return retries > 0
? fetchWithRetry(url, retries - 1)
: Promise.reject(err);
});
}
通过这套方案,我们实现了:
- 单页面支持混合数据源展示
- 5秒级数据刷新无感知切换
- 百万级数据流畅渲染
- 完整的错误恢复机制
更多推荐



所有评论(0)