基于Hadoop+HBase+Spring Boot+Vue的天气数据分析与可视化系统

一、项目概述

本项目是一个基于大数据技术的天气数据分析与可视化系统,采用Hadoop分布式计算框架处理海量天气数据,使用HBase进行高性能数据存储,通过Spring Boot构建RESTful API服务,并利用Vue.js+ECharts实现数据可视化展示。系统支持全国多个城市的天气数据采集、存储、分析和可视化展示,为气象研究和决策提供数据支持。

1.1 项目架构

weather-analysis-system/
├── weather-mapreduce/      # MapReduce数据分析模块
├── weather-backend/        # Spring Boot后端API服务
├── weather-frontend/       # Vue.js前端可视化
└── weather-data-service/   # 数据服务模块

1.2 技术栈

后端技术栈:

  • Spring Boot 2.7.18:Web框架
  • Hadoop 3.1.0:分布式计算框架
  • HBase 2.1.0:分布式NoSQL数据库
  • MyBatis 3.5.13:持久层框架
  • Spring Security 5.7.12:安全框架
  • Log4j2:日志框架

前端技术栈:

  • Vue 3.3.4:渐进式JavaScript框架
  • Vue Router 4.2.5:路由管理
  • Vuex 4.1.0:状态管理
  • ECharts 5.4.3:数据可视化库
  • Element Plus 2.4.0:UI组件库
  • Axios 0.27.2:HTTP客户端

大数据组件:

  • HDFS:分布式文件系统,存储原始数据和分析结果
  • HBase:分布式列式数据库,存储天气数据和统计结果
  • MapReduce:分布式计算框架,进行数据分析

二、核心功能模块

2.1 数据采集与存储

系统支持CSV格式天气数据的导入,数据包含以下字段:

  • 城市信息:城市代码、城市名称、省份、国家
  • 温度数据:温度、体感温度
  • 湿度数据:湿度
  • 风力数据:风速、风向、风力等级
  • 气压数据:气压
  • 能见度数据:能见度
  • 紫外线数据:紫外线指数
  • 其他数据:日出日落时间、天气提示等

2.2 MapReduce数据分析

系统实现了多个MapReduce作业,用于分析不同维度的天气数据:

温度分析:

  • Mapper:提取城市代码和温度数据
  • Reducer:计算平均温度、最高温度、最低温度、标准差
  • 输出格式:JSON格式的统计数据

湿度分析:

  • Mapper:提取城市代码和湿度数据
  • Reducer:计算平均湿度、最高湿度、最低湿度

风速分析:

  • Mapper:提取城市代码和风速数据
  • Reducer:计算平均风速、最大风速、最小风速

气压分析:

  • Mapper:提取城市代码和气压数据
  • Reducer:计算平均气压、最高气压、最低气压

能见度分析:

  • Mapper:提取城市代码和能见度数据
  • Reducer:计算平均能见度、最高能见度、最低能见度

紫外线指数分析:

  • Mapper:提取城市代码和紫外线指数数据
  • Reducer:计算平均紫外线指数、最高紫外线指数

体感温度差异分析:

  • Mapper:提取城市代码、实际温度和体感温度
  • Reducer:计算体感温度与实际温度的平均差异、最大差异

2.3 RESTful API服务

系统提供完整的RESTful API接口,包括:

天气数据接口:

  • GET /weather/raw/{rowKey}:获取指定行键的原始天气数据
  • GET /weather/raw/city/{cityCode}:获取指定城市的原始天气数据列表
  • GET /weather/stats/{cityCode}/{dataType}:获取指定城市的统计数据
  • POST /weather/raw:插入原始天气数据
  • POST /weather/stats:插入统计数据
  • PUT /weather/raw/{rowKey}:更新原始天气数据
  • PUT /weather/stats/{rowKey}:更新统计数据
  • DELETE /weather/raw/{rowKey}:删除原始天气数据
  • DELETE /weather/stats/{rowKey}:删除统计数据
  • GET /weather/history/{cityCode}:获取指定城市的历史天气数据

统计接口:

  • GET /stats/dashboard:获取仪表板统计数据
  • GET /stats/national/average:获取全国平均天气数据
  • GET /stats/national/extremes:获取全国极值天气数据
  • GET /stats/all/{statsType}:获取所有城市的特定类型统计数据

2.4 数据可视化

系统提供丰富的数据可视化功能:

仪表盘:

  • 数据统计卡片:平均温度、平均湿度、平均风速、原始数据量、统计数据量
  • 全国极值数据:最高温度、最低温度、最高湿度、最低湿度、最大风速、最小风速
  • 全国平均数据:平均温度、平均湿度、平均风速
  • 城市数据详情表格:支持温度、湿度、风速三个维度的数据展示
  • 多种图表:温度分布柱状图、湿度分布柱状图、风速分布柱状图、城市数据量环形图、温度湿度散点图、城市天气雷达图、全国天气指标折线图、城市数据词云图

温度分析:

  • 温度统计卡片:平均温度、最高温度、最低温度
  • 温度趋势图表:展示温度随时间的变化趋势
  • 温度分布图表:展示温度在不同区间的分布情况
  • 城市温度对比图表:对比不同城市的平均温度
  • 温度详细数据表格:展示具体的温度数据

三、核心源码分析

3.1 HBase连接管理

HBaseUtil工具类负责管理HBase连接,提供连接池和基本操作方法:

@Component
public class HBaseUtil {
    
    private static Connection connection = null;
    
    private static final String HBASE_QUORUM = "192.168.101.101";
    private static final String HBASE_PORT = "2181";
    
    public static synchronized Connection getConnection() throws IOException {
        if (connection == null || connection.isClosed()) {
            Configuration config = HBaseConfiguration.create();
            config.set("hbase.zookeeper.quorum", HBASE_QUORUM);
            config.set("hbase.zookeeper.property.clientPort", HBASE_PORT);
            config.set("hbase.master", HBASE_QUORUM + ":16000");
            config.set("hbase.security.authentication", "simple");
            config.set("hadoop.security.authentication", "simple");
            config.set("hbase.client.scanner.timeout.period", "600000");
            config.set("hbase.rpc.timeout", "600000");
            config.set("zookeeper.session.timeout", "600000");
            config.set("zookeeper.recovery.retry.max", "3");
            connection = ConnectionFactory.createConnection(config);
        }
        return connection;
    }
}

技术要点:

  1. 使用单例模式管理HBase连接,避免频繁创建和销毁连接
  2. 配置了连接超时和重试机制,提高系统的稳定性
  3. 禁用了安全认证,简化开发环境配置

3.2 数据访问层

HBaseWeatherDao实现了数据的CRUD操作,支持缓存机制:

@Repository
@CacheConfig(cacheNames = "hbaseWeatherCache")
public class HBaseWeatherDao {
    
    @Autowired
    private HBaseUtil hbaseUtil;
    
    private static final String TABLE_RAW = "weather_raw";
    private static final String TABLE_STATS = "weather_stats";
    private static final String FAMILY_RAW = "info";
    private static final String FAMILY_STATS = "stats";
    
    @Cacheable(key = "'raw_' + #rowKey")
    public Map<String, String> getRawDataByRowKey(String rowKey) throws IOException {
        return HBaseUtil.getDataByRowKey(TABLE_RAW, rowKey);
    }
    
    @Cacheable(key = "'stats_' + #statsType + '_' + #cityCode")
    public Map<String, String> getStatsData(String cityCode, String statsType) throws IOException {
        return HBaseUtil.getStatsData(statsType, cityCode);
    }
    
    public Map<String, Object> getDashboardStats() throws IOException {
        Map<String, Object> dashboardStats = new HashMap<>();
        List<Map<String, String>> resultList = HBaseUtil.scanTable(TABLE_STATS, null, null);
        
        double totalTemp = 0.0;
        double totalHumidity = 0.0;
        double totalWindSpeed = 0.0;
        double maxTemp = Double.MIN_VALUE;
        double minTemp = Double.MAX_VALUE;
        int tempCityCount = 0;
        
        for (Map<String, String> rowData : resultList) {
            String rowKey = rowData.get("rowKey");
            if (rowData.containsKey("stats:temperature_stats")) {
                String tempJson = rowData.get("stats:temperature_stats");
                double avgTemp = extractValueFromJson(tempJson, "avgValue");
                double maxCityTemp = extractValueFromJson(tempJson, "maxValue");
                double minCityTemp = extractValueFromJson(tempJson, "minValue");
                
                totalTemp += avgTemp;
                maxTemp = Math.max(maxTemp, maxCityTemp);
                minTemp = Math.min(minTemp, minCityTemp);
                tempCityCount++;
            }
        }
        
        dashboardStats.put("averageTemperature", totalTemp / tempCityCount);
        dashboardStats.put("highestTemperature", maxTemp);
        dashboardStats.put("lowestTemperature", minTemp);
        
        return dashboardStats;
    }
}

技术要点:

  1. 使用Spring Cache注解实现缓存,提高查询性能
  2. 实现了JSON数据的解析和统计计算
  3. 支持批量数据扫描和聚合计算

3.3 MapReduce温度分析

TemperatureMapper负责解析CSV数据并提取温度信息:

public class TemperatureMapper extends Mapper<LongWritable, Text, Text, DoubleWritable> {
    
    private Text cityKey = new Text();
    private DoubleWritable temperature = new DoubleWritable();
    
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        if (key.get() == 0) {
            return;
        }
        
        String line = value.toString();
        if (line.isEmpty()) {
            return;
        }
        
        line = line.replace("\"", "");
        String[] parts = line.split(",");
        
        if (parts.length >= 9) {
            String cityCode = parts[1];
            String tempStr = parts[8];
            
            if (tempStr != null && !tempStr.isEmpty()) {
                try {
                    double temp = Double.parseDouble(tempStr);
                    cityKey.set(cityCode);
                    temperature.set(temp);
                    context.write(cityKey, temperature);
                } catch (NumberFormatException e) {
                    context.getCounter("TemperatureMapper", "INVALID_TEMP").increment(1);
                }
            }
        }
    }
}

技术要点:

  1. 跳过CSV表头,只处理数据行
  2. 使用城市代码作为Map的Key,温度作为Value
  3. 添加计数器统计无效数据

TemperatureReducer负责计算温度统计值:

public class TemperatureReducer extends Reducer<Text, DoubleWritable, Text, Text> {
    
    @Override
    protected void reduce(Text key, Iterable<DoubleWritable> values, Context context) throws IOException, InterruptedException {
        double sum = 0;
        double max = Double.MIN_VALUE;
        double min = Double.MAX_VALUE;
        long count = 0;
        List<Double> tempList = new ArrayList<>();
        
        for (DoubleWritable value : values) {
            double temp = value.get();
            sum += temp;
            count++;
            
            if (temp > max) {
                max = temp;
            }
            if (temp < min) {
                min = temp;
            }
            
            tempList.add(temp);
        }
        
        double avg = sum / count;
        
        double stdDev = 0;
        for (Double temp : tempList) {
            stdDev += Math.pow(temp - avg, 2);
        }
        stdDev = Math.sqrt(stdDev / count);
        
        String output = String.format("{\"statsType\":\"temp\",\"cityCode\":\"%s\",\"avgValue\":%.2f,\"maxValue\":%.2f,\"minValue\":%.2f,\"stdDev\":%.2f,\"count\":%d}",
                key.toString(), avg, max, min, stdDev, count);
        
        context.write(key, new Text(output));
    }
}

技术要点:

  1. 计算平均值、最大值、最小值、标准差等统计指标
  2. 输出JSON格式的结果,便于后续处理
  3. 使用List存储温度值用于标准差计算

3.4 REST API控制器

WeatherController提供天气数据的RESTful API:

@RestController
@RequestMapping("/weather")
public class WeatherController {
    
    @Autowired
    private HBaseWeatherDao weatherDao;
    
    @GetMapping("/raw/{rowKey}")
    public ResponseEntity<?> getRawDataByRowKey(@PathVariable String rowKey) {
        try {
            Map<String, String> data = weatherDao.getRawDataByRowKey(rowKey);
            if (data.isEmpty()) {
                return ResponseEntity.status(HttpStatus.NOT_FOUND).body("天气数据不存在");
            }
            return ResponseEntity.ok(data);
        } catch (IOException e) {
            throw new WeatherServiceException("获取天气数据失败: " + e.getMessage());
        }
    }
    
    @GetMapping("/raw/city/{cityCode}")
    public ResponseEntity<?> getRawDataByCityCode(
            @PathVariable String cityCode,
            @RequestParam(defaultValue = "10") int limit) {
        try {
            List<Map<String, String>> dataList = weatherDao.getRawDataByCityCode(cityCode, limit);
            if (dataList.isEmpty()) {
                return ResponseEntity.status(HttpStatus.NOT_FOUND).body("该城市天气数据不存在");
            }
            return ResponseEntity.ok(dataList);
        } catch (IOException e) {
            throw new WeatherServiceException("获取城市天气数据失败: " + e.getMessage());
        }
    }
}

技术要点:

  1. 使用Spring MVC注解定义RESTful接口
  2. 统一的异常处理机制
  3. 支持分页查询

3.5 前端数据可视化

Dashboard.vue实现了数据可视化仪表盘:

export default {
  name: 'Dashboard',
  setup() {
    const dashboardStats = ref(null)
    const nationalExtremes = ref(null)
    const nationalAverage = ref(null)
    const citiesTemperature = ref([])
    const citiesHumidity = ref([])
    const citiesWindSpeed = ref([])
    
    const fetchWeatherStats = async () => {
      loading.value = true
      error.value = false
      
      try {
        const [statsResponse, extremesResponse, averageResponse, temperatureResponse, humidityResponse, windSpeedResponse] = await Promise.all([
          axios.get('/stats/dashboard'),
          axios.get('/stats/national/extremes'),
          axios.get('/stats/national/average'),
          axios.get('/stats/all/temperature'),
          axios.get('/stats/all/humidity'),
          axios.get('/stats/all/windSpeed')
        ])
        
        dashboardStats.value = statsResponse
        nationalExtremes.value = transformedExtremes
        nationalAverage.value = transformedAverage
        citiesTemperature.value = transformedTemperature
        citiesHumidity.value = transformedHumidity
        citiesWindSpeed.value = transformedWindSpeed
        
        updateAllCharts()
      } catch (err) {
        error.value = true
        ElMessage.error('数据加载失败')
      } finally {
        loading.value = false
      }
    }
    
    const initTemperatureChart = () => {
      temperatureChart = echarts.init(chartDom)
      
      const option = {
        title: { text: '城市温度分布', left: 'center' },
        tooltip: { trigger: 'axis' },
        xAxis: { type: 'category', data: cities },
        yAxis: { type: 'value', name: '温度 (°C)' },
        series: [{
          name: '平均温度',
          type: 'bar',
          data: temperatures,
          itemStyle: {
            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
              { offset: 0, color: '#ff6b6b' },
              { offset: 1, color: '#ff8e8e' }
            ])
          }
        }]
      }
      
      temperatureChart.setOption(option)
    }
  }
}

技术要点:

  1. 使用Vue 3 Composition API
  2. 使用ECharts实现多种图表类型
  3. 并行请求多个API,提高性能
  4. 响应式数据绑定和状态管理

3.6 前端HTTP客户端

axios.js配置了HTTP请求拦截器:

import axios from 'axios'

const service = axios.create({
  baseURL: '/api',
  timeout: 30000
})

service.interceptors.response.use(
  response => {
    return response.data
  },
  error => {
    console.error('响应错误:', error)
    return Promise.reject(error)
  }
)

export default service

技术要点:

  1. 创建axios实例,统一配置baseURL和超时时间
  2. 使用响应拦截器简化数据处理
  3. 统一的错误处理机制

四、技术亮点

4.1 大数据处理能力

系统采用Hadoop MapReduce框架进行分布式计算,能够处理海量天气数据:

  • 支持百万级数据量的处理
  • 分布式并行计算,提高处理效率
  • 可扩展性强,支持集群部署

4.2 高性能数据存储

使用HBase作为数据存储层,具有以下优势:

  • 分布式列式存储,适合海量数据
  • 支持快速随机读写
  • 良好的扩展性,支持水平扩展
  • 支持数据版本控制

4.3 缓存机制

系统实现了多层缓存机制:

  • Spring Cache注解实现方法级缓存
  • HBase连接池管理
  • 前端数据缓存和状态管理

4.4 可视化展示

系统提供了丰富的数据可视化功能:

  • 支持多种图表类型:柱状图、折线图、散点图、雷达图、词云图等
  • 交互式图表,支持缩放、筛选等功能
  • 响应式设计,适配不同屏幕尺寸

4.5 RESTful API设计

系统遵循RESTful API设计规范:

  • 统一的接口风格
  • 清晰的资源命名
  • 标准的HTTP方法使用
  • 完善的错误处理机制

五、部署架构

5.1 系统架构图

┌─────────────┐
│   前端Vue   │
│  (Nginx)    │
└──────┬──────┘
       │ HTTP
┌──────▼─────────────────┐
│  Spring Boot后端       │
│  (RESTful API)         │
└──────┬─────────────────┘
       │
┌──────▼─────────────────┐
│  HBase                 │
│  (NoSQL数据库)         │
└──────┬─────────────────┘
       │
┌──────▼─────────────────┐
│  Hadoop HDFS           │
│  (分布式文件系统)       │
└────────────────────────┘

5.2 环境配置

虚拟机环境:

  • 操作系统:CentOS 7
  • 内存:4GB
  • 处理器:2核
  • 磁盘空间:20GB
  • 节点:192.168.101.101

大数据环境:

  • Hadoop 3.3.4(192.168.101.101:8020)
  • Zookeeper 3.8.0
  • HBase 2.4.13
  • MySQL 8.0.33

数据库配置:

  • 主机:192.168.101.101
  • 端口:3306
  • 用户名:root
  • 密码:123456

六、总结

本项目实现了一个完整的天气数据分析与可视化系统,具有以下特点:

  1. 技术先进:采用Hadoop+HBase+Spring Boot+Vue等主流技术栈
  2. 功能完善:涵盖数据采集、存储、分析、可视化等完整流程
  3. 性能优越:支持海量数据处理,具有良好的扩展性
  4. 可视化丰富:提供多种图表类型,直观展示数据分析结果
  5. 架构清晰:前后端分离,模块化设计,便于维护和扩展

该系统为气象数据分析和决策提供了有力的技术支持,具有广阔的应用前景。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐