用户行为数据可视化

8.1 构建javaweb系统查询用户行为

8.1.2 搭建并部署javaweb项目

tomcat的安装与配置

进入网址:https://tomcat.apache.org/download-90.cgi,选择图示版本

解压至windows本地文件夹即可

idea构建javaweb项目

在Idea中打开书中配套代码

点编辑配置

点配置,选择自己的文件夹

添加部署

添加完成将下述路径改成/newsWeb

将web.xml文件改成如下内容:

启动tomcat

pom.xml文件的json-lib库改成:

xml 复制代码
    <dependency>
      <groupId>net.sf.json-lib</groupId>
      <artifactId>json-lib</artifactId>
      <version>2.4</version>
      <classifier>jdk15</classifier>
    </dependency>

启动之后:

访问网址如下:

http://localhost:8080/newsWeb/TestServlet

如图

8.1.3 用户行为查询代码开发

修改配置文件

修改my.properties

修改web.xml文件

8.2 用户行为数据展示与分析

javaweb项目配置与打包

把my.properties改成:

properites 复制代码
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.datasource.size=5
jdbc.url=jdbc:mysql://192.168.255.139:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
jdbc.user=hive
jdbc.password=hive

JDBCHelper.java改成:

java 复制代码
//第一步:加载驱动
	/*static{

		try {
			ConfigurationManager.getProperty(Constants.JDBC_DRIVER);
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}*/
	static {
		try {
			// 直接用配置文件里的值,适配 8.x
			Class.forName(ConfigurationManager.getProperty(Constants.JDBC_DRIVER));
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

web.xml改成:

xml 复制代码
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>newsWeb</display-name>

  <servlet>
    <servlet-name>NewsSvlt</servlet-name>
    <servlet-class>com.djt.servlet.NewsSvlt</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>NewsSvlt</servlet-name>
    <url-pattern>/news</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>jsp/news.jsp</welcome-file>
  </welcome-file-list>
</web-app>

在webapp下新建目录jsp,在此下新建文件news.jsp

jsp 复制代码
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>新闻大屏展示</title>
    <!-- 引入 ECharts 和 jQuery -->
    <script src="<%=path%>/js/echarts.min.js"></script>
    <script src="<%=path%>/js/jquery-3.2.1.js"></script>
</head>
<body>
<div id="loading" style="text-align:center;padding:50px;font-size:18px;color:#666;">
    正在加载新闻数据...
</div>
<div id="content" style="display:none;">
    <div id="main" style="width:700px;height:420px;float:left;"></div>
    <div id="sum" style="width:730px;height:420px;float:left;"></div>
    <div id="period" style="width:1430px;height:250px;float:left;"></div>
</div></body>

<script type="text/javascript">
    var myChart, myChart_sum, myChart_period;
    
    function initCharts() {
        myChart = echarts.init(document.getElementById('main'));
        myChart_sum = echarts.init(document.getElementById('sum'));
        myChart_period = echarts.init(document.getElementById('period'));
    }

    $(document).ready(function(){
        // 初始化图表
        initCharts();
        
        // 加载数据
        initNewsNum();
        
        // 每10秒刷新一次(减少频率避免过多请求)
        setInterval(function() {
            initNewsNum();
        }, 10000);
    });

    function initNewsNum(){
        var action = "<%=path%>/news";   // 调用后端 Servlet
        
        $.ajax({
            url: action,
            type: 'GET',
            dataType: 'json',
            timeout: 10000,
            success: function(data) {
                console.log('接收到数据:', data);
                if (data.error) {
                    console.error('服务器返回错误:', data.error);
                    return;
                }
                
                // 隐藏加载提示,显示内容
                $('#loading').hide();
                $('#content').show();
                
                newsRank(data);
                newsSum(data.newssum);
                periodRank(data);
            },
            error: function(xhr, status, error) {
                console.error('AJAX请求失败:', status, error);
                console.error('响应内容:', xhr.responseText);
                
                // 隐藏加载提示,显示错误信息
                $('#loading').html('<div style="color:red;font-size:16px;">数据加载失败,请检查数据库连接<br/>错误信息: ' + error + '</div>');
                
                // 可选:显示空的图表
                $('#content').show();
                var emptyData = {
                    name: [],
                    newscount: [],
                    logtime: [],
                    periodcount: [],
                    newssum: 0
                };
                
                newsRank(emptyData);
                newsSum(emptyData.newssum);
                periodRank(emptyData);
            }
        });
    }

    // 新闻浏览量排行
    function newsRank(json){
        var option = {
            backgroundColor: '#ffffff',
            title: {
                text: '新闻话题浏览量【实时】排行',
                subtext: '数据来自搜狗',
                textStyle: { fontWeight: 'normal', color: '#408829' }
            },
            tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
            legend: { data: ['浏览量'] },
            grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
            xAxis: { type: 'value', boundaryGap: [0, 0.01] },
            yAxis: { type: 'category', data: json.name },
            series: [{
                name: '浏览量',
                type: 'bar',
                label: { normal: { show: true, position: 'insideRight' } },
                itemStyle:{ normal:{color:'#f47209'} },
                data: json.newscount
            }]
        };
        myChart.setOption(option);
    }

    // 新闻话题曝光总量
    function newsSum(data){
        var option = {
            backgroundColor: '#fbfbfb',
            title: { text: '新闻话题曝光量【实时】统计', subtext: '数据来自搜狗' },
            tooltip: { formatter: "{a} <br/>{b} : {c}%" },
            toolbox: { feature: { restore: {}, saveAsImage: {} } },
            series: [{
                name: '业务指标',
                type: 'gauge',
                max: 50000,
                detail: { formatter:'{value}个话题' },
                data: [{value: 0, name: '话题曝光量'}]
            }]
        };
        option.series[0].data[0].value = data;
        myChart_sum.setOption(option, true);
    }

    // 时段新闻浏览量排行
    function periodRank(json){
        var option = {
            backgroundColor: '#ffffff',
            color: ['#00FFFF'],
            tooltip : { trigger: 'axis', axisPointer : { type : 'shadow' } },
            grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
            xAxis : [{ type : 'category', data : json.logtime, axisTick: { alignWithLabel: true } }],
            yAxis : [{ type : 'value' }],
            series : [{
                name:'新闻话题曝光量',
                type:'bar',
                barWidth: '60%',
                data: json.periodcount
            }]
        };
        myChart_period.setOption(option, true);
    }
</script>
</body>
</html>

把打包好的拷贝到tomcat的webapps目录下:

启动数据库

确认hadoop01的数据库启动,并在tomcat里把表清空

启动

来到tomcat的bin目录下,双击start.bat

访问 http://localhost:8080/newsWeb/

此时没数据所以显示空白

8.2.2 项目整体联调

启动 MySQL 服务

因为 Flink 处理结果要落到 MySQL 的 newscountperiodcount 表里,所以必须先启动数据库。

  • hadoop01 节点执行:

    bash 复制代码
    service mysql start

    bash 复制代码
    systemctl start mysqld
  • 验证:

    bash 复制代码
    mysql -uroot -p
    mysql> show databases;

确保有 test 数据库,以及表 newscountperiodcount 已创建。

启动 Zookeeper 服务

Kafka 依赖 Zookeeper 协调。

  • hadoop01/hadoop02/hadoop03 各节点执行:

    bash 复制代码
    cd /home/hadoop/app/zookeeper/bin
    ./zkServer.sh start
  • 验证:

    bash 复制代码
    ./zkServer.sh status

    确保有一个是 leader,其他是 follower


启动 Kafka 集群

Flink DataStream 会消费 Kafka 的日志流。

  • 在各节点执行:

    bash 复制代码
    cd /home/hadoop/app/kafka/bin
    ./kafka-server-start.sh -daemon ../config/server.properties
  • 验证:

    bash 复制代码
    jps | grep Kafka

书里用的例子是 KafkaFlinkMySQL,把 Kafka 数据实时处理后写入 MySQL。

  • 在 IDEA 里找到 KafkaFlinkMySQL.java
  • 右键 → Run 'KafkaFlinkMySQL.main()'
  • Flink 应用会本地运行,消费 Kafka 日志,计算新闻浏览量 → 写入 MySQL。

启动 Flume 聚合节点

Flume 负责把采集到的日志推送到 Kafka。

  • hadoop02hadoop03 上执行:

    bash 复制代码
    cd /home/hadoop/app/flume
    bin/flume-ng agent -n a2 -c conf -f conf/xxx.conf -Dflume.root.logger=INFO,console &

启动 Flume 采集节点

  • hadoop01 上执行:

    bash 复制代码
    cd /home/hadoop/app/flume
    bin/flume-ng agent -n a1 -c conf -f conf/xxx.conf -Dflume.root.logger=INFO,console &

这一步会把日志源源不断地推送到聚合节点,再写到 Kafka。

模拟产生数据

  • hadoop01 上执行:

    bash 复制代码
    cd /home/hadoop/shell/bin
    ./sogoulogs.sh

查看 MySQL 结果


最终效果

  1. Flume → Kafka → Flink → MySQL → Java Web 大屏

  2. 打开大屏地址:

    复制代码
    http://localhost:8080/newsWeb/

    页面会通过 NewsSvlt 查询 MySQL → 返回 JSON → ECharts 实时展示统计结果。


访问http://localhost:8080/newsWeb/

相关推荐
励志成为美貌才华为一体的女子4 小时前
每日AI学习笔记----Qwen3-Omni 、HuatuoGPT-o1医学复杂推理
笔记·学习
难以怀瑾4 小时前
Appium笔记
笔记·appium
喝奶茶的Blair7 小时前
PHP应用&文件操作安全&上传下载&任意读取删除&目录遍历&文件包含(2024小迪安全Day32笔记)
笔记·安全·web安全·php
林森见鹿7 小时前
人机协同新范式:当“知行合一”遇见人工智能
笔记
无损去水印精灵8 小时前
抖音视频图片如何去水印?去水印工具分享
经验分享·笔记·算法·音视频
Duo1J9 小时前
【OpenGL】LearnOpenGL学习笔记25 - 法线贴图 NormalMap
笔记·学习·图形渲染·贴图·着色器
Duo1J9 小时前
【OpenGL】LearnOpenGL学习笔记26 - 视差贴图 Parallax Map
笔记·学习·图形渲染·贴图·着色器
风已经起了10 小时前
FPGA学习笔记——图像处理之亮度调节(乘法型)
图像处理·笔记·学习·fpga开发·fpga
能不能别报错10 小时前
K8s学习笔记(五) Velero结合minnio业务数据备份与恢复
笔记·学习·kubernetes