微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

基于Spark的交互式大数据预处理系统设计与实现七  Web站点搭建数据可视化

分享学习我的本科毕业论文,欢迎指教。

  Web站点开发概述

在web端主要负责从集群获取数据进行展示以及简单的数据图形化操作。获取数据沿用kafka工具实时从集群获取数据流处理结果,而后展示至web页面。为了快捷开发,本系统采用springboot为基础架构,实现的依旧是传统mvc模式,但基于springboot的架构省去了繁琐的配置文件编写及其他糅杂工作,因而大大加快了系统总体开发进程。为了更加友好地展示数据文件及数据处理过程,本系统运用echarts进行数据图形化展示,此外为了更加具体化展示数据预处理将原始数据即流数据与处理结果以文本化展示至界面。在基于kafka数据源的情况下,本系统开发了KafkaTool担任service从kafka(topic:resource/dist)取得数据源。而后利用ajax定时异步请求controller从service获取数据,由于本系统在集群数据处理端以5秒为一个batch进行数据处理,故而web端的ajax异步请求时间间隔也应设置为5秒,即web端数据刷新频率需要与集群处理频率同步,如此方能在不浪费网络通信资源的情况下及时获取最新处理结果数据。

  数据获取

本系统web站点模块沿用Springboot搭建,web站点与集群核心数据接口为kafkaService.KafkaTool,该接口依赖于entity.WebLogPro(结果数据集及相关操作)、entity.OriginalLogPro(原始数据集及相关操作)对象。而在entity.WebLogPro对象中封装了对entity.WebLog数据集List<WebLog>的一系列操作entity.OriginalLogPro对象中封装了数据集List<String>的一系列操作。这两个对象除了向kafkaService.Kafka

Tool提供了基本的数据集操作外还需提供基本的数据同步机制,详细如下所示。

kafkaService.KafkaTool本身负责从kafka(topic:resource/dist)中取得来自集群数据处理平台的原始/结果数据,在启动多个线程处理实时数据batch的同时亦需要提供对外的结果数据集访问服务。在此先对结果数据获取依赖对象即entity.WebLogPro进行详细论述。在entity.WebLogPro对象中需要完成对唯一结果数据集(List<WebLog> logs,以下简称logs)的更新操作,由于在kafka中会启动多个线程对logs进行修改,故而需要一个同步锁机制,此处直接简单的使用synchronized关键字修饰操作,代码示例如下:

  synchronized(logs){

           //process statement for logs

        }

在entity.WebLogPro对象中针对两种不同的需求开发了两种不同logs操作模式,总体而言即在kafka中拿到每一个批次的数据处理结果合并到web站点数据集logs中,在前文介绍中可知logs中的数据模式(province,value):其中province为34所省级单位且为数据集关键字,此时在合并操作中有两种方式:一种是将集群取得的数据直接按照34所省级单位为关键字替换更新其对应单位访问统计值value 。另一种是将集群取得的数据按照34所省级单位为关键字累加更新对应访问统计值value。即其区别仅在于对对应关键字的访问统计值是否做累加。举例如下:在假设当前仅有一个线程执行batch数据读取的理想情况下,web数据logs中包含(上海,198),在该时刻stream批次中从集群取得数据(上海,2),若执行直接替换(noacc)更新操作则得到logs中包含(上海,2)这条数据,而若是执行累加更新(acc)操作则得到logs中包含(上海,200)这条数据。核心代码如下:

 //update the dataset with accumulation

 public void addLogsAcc(List<WebLog> logs){

       for(WebLog log:logs){

           this.addLogAcc(log);

       }

 }

 //update the dataset with accumulation

 public void addLogAcc(WebLog log){

        synchronized(logs){

            if(this.logs.contains(log)){//accumulation data value

                this.logs.get(this.logs.indexOf(log)).addValue(log);

            }

            else{

                this.logs.add(log);

            }

        }

 }

 //add/update data to logs

 public void addLogs(List<WebLog> logs){

        for(WebLog log:logs){

            this.addLog(log);

        }

 }

 //modify the dataset without accumulation

 public void addLog(WebLog log){

        synchronized (logs){

            if(this.logs.contains(log)){

                //if existed,update it               this.logs.get(this.logs.indexOf(log)).setValue(log.getValue());

            }

            else

            {

                this.logs.add(log);

            }

        }

 }

其实综合上文可知,在集群中也维护了一个类似于logs的数据集,并且也有对应acc/noacc操作,在一定层面上这其实是针对同一数据流的同一类型操作。只不过集群的数据集处于数据流的上游,web端的数据集属于数据流的下游。之所以会有这么一个区分最初是为了测试数据,而今邻近整个系统初现雏形,在此发现可以运用数据上下游的组合操作进行负载均衡调控以及适应不同的运用场合及用户

对此,详细论述如下所示:若是按照传统单节点web服务器此番思考似乎没有太大意义,但分布式微服务架构的兴起让这个思考变得有迹可循。在上述背景下web站点可以开发成分布式集群模式运行,数据处理运算平台也是集群模式运行。在海量数据场景下,经过预处理但未经聚合的数据量是非常庞大的,试想若处于数据流上游的集群数据处理平台将数据完成了聚合操作,那自然可以降低后续的数据流通信及运算成本。此时web服务集群亦不需要进行acc聚合操作,而是直接做noacc替换更新操作;数据运算处理任务由集群数据运算平台承担,这种运算场景适应于大多数的普通用户,他们不需要过多关心数据处理细节。就像上面所述,在大多数运用场景下由数据流处理上游完成数据聚合是比较好的选择(在此并未考虑将上游数据单独开启线程持久化至redis之类的数据库中,而是在实时反馈性要求较高的情况下)。但在对于一些需要获取上游数据的专业用户来说,则不应在上游将数据聚合,而是将数据发送到下游由用户自行处理,当然在此场景下也可通过RPC让负载迂回数据上游。

在本系统中,web端是单节点服务的并未采用分布式,但因为是在实验环境下数据量比较小且考虑到web端原始数据预处理数据展示的需要及调试的方便性,采用的是上述后一种组合,即在数据流下游进行数据聚合操作。在此情形下,web端需要获取一个批次的处理结果并在服务端完成数据聚合。系统模块数据流如下图5-8所示:

图5-8 系统模块数据流

  数据展示

综合本系统实际情况,分为如下几个数据展示模块。

实时刷新:(属性频率与前述batch截取频率一致)的图形化界面;采用echarts中国地图+饼状图的模式可在一定程度上实现数据交互式展示。用户可以查看某个省的访问统计情况,亦可以选择一批省份进行图形化分析,如各个省份在当前选中省级单位访问量所占比率。在web前端运用ajax从web后端数据集获取数据代码核心如下:

$.ajax({

        type:"POST",

        cache:false,

        url:"/weblog/findAllFromKafkadist",

        dataType: "json",

        success:function (result) {

            //set data from result to echarts

            //set option data           

            myChart.setoption(option);

      }

});

图形展示效果如下图5-9所示:

图5-9 数据图形化展示效果

实时展示原始数据产生情况:同样的采用kafka消息中间件将数据从集群取得(该数据来源自sparkStream实时计算中模拟生成并写入到kafka(topic:resource)中)而后利用jquery的dom编程操作实时展示至web前端,核心代码如下所示:

$.ajax({

            type:"POST",

            cache:false,

            url:"/weblog/findAllFromKafkaOriginal",

            dataType: "json",

            async:true,

            success:function(data)

            {

                count++;//counter

                var timec=new Date();

                for (var i=0 ;i< data.length;i++)

                {

                    $("#resultData").prepend(' <li class="words-reads-li"><p class="words-reads-content">(' +data[i].name+

','+data[i].value+')</p></li>');

                }

                $("#resultData").prepend('<span

class="split-line">' + timec.toLocaleString()+

':第'+count+'批次</span>');

   } });

代码可以看到,此处结合ajax+jquery的dom编程完成对数据的异步请求、异步修改界面内容,原始数据展示如下图5-10所示:

图5-10 原始数据展示效果

实时展示经降维选取的字段数据:该阶段会将经过第二阶段预处理的数据分成两批,一批为异常数据(错误数据,非法ip,空值等);另一批为无误的目标数据。无误的数据将在下一阶段写入中写入kafka(topic:dist)中,异常数据将在数据预处理阶段过滤去除。由于在此并未将含有异常数据阶段的数据持久化或写入kafka,实际上用户也并不需要知道数据处理的过程,也不需要关注数据源长成什么样子,只要知道按需经过处理分析后的结果数据即可,故而本阶段数据留在集群控制台测试显示。当然在另一方面,对于许多要求较高的系统来说异常数据的采集同样具有分析价值,甚至需要将原始数据、异常数据、预处理后数据区分持久化至数据仓库。而由于本系统基于实验环境下开发且受限于资源情况,故而没有做到如此高规格,但上述问题在实际的生产环境中是需要考虑到的。

实时展示经处理后的数据:在此实际上是图形化数据的文本化展示。具体代码与上述原始数据获取类似在此不再展示,结果数据展示如下图5-11所示:

图5-11 结果数据展示效果

综合上述在进行web展示原始数据模块中需要注意以下问题。由于Web页面需要展示处理前的原始数据,原始数据来源多样且皆为半结构化字符串,如前文所述,原始数据来源多样且长度变化波动较大。此外本系统目标数据在于字符串切分的前几个字段,前端仅需看到处理前的所需要看到的参照数据即可。故而在此不会将每个批次的原始数据字符串完整拉取,而是每个字符串截取其前部分固定长度的子串展示至web页面

此外在web端的原始数据获取时,由于每次传送会将全体数据进行传送,且没有关键字对照进行上一批次数据的清除。在此需要在每次获取数据的时候对dataset进行数据清除操作。清除操作的时机选择很重要,早了,数据没读取展示至页面就被清除了;晚了,数据重复展示。在此选择在ajax请求kafkaService获取数据集中对该数据集进行清除,但由于对于数据集的写操作加上了同步锁机制,故而在此有可能会丢失一条数据:在获取数据的时候kafka刚好获得一条数据开始往数据集里写,得到数据需要清除源数据集的时候需等待本条数据写完了再进行清除,从而导致一条数据丢失,虽然数据在展示至前端时有丢失,但依旧不影响整体数据对照展示。实际上,由于本阶段的数据读取与集群数据处理源数据并不是同一个kafka topic组,因而不会对集群数据处理结果造成任何偏差,在此丢失数据影响的也仅仅是前端用户在极其细心前提下的一丝丝观察体验,而本阶段的原始数据展示对于本系统的实际运用来说并没有太大的功能性意义,在此也仅仅是为了在开发学习中方便数据观察实验的需要。

虽然本阶段的逻辑简单,但还是有一些问题值得注意,比如在清除数据集之前需要获取该数据集作为前端ajax请求响应。如果只是简单的将对象赋值的话前端是无法获取数据的,因为该对象对应的数据集已经在前端返回之前就被清除了,故而在此需要用到“深拷贝”,即在清除本批次原始数据集之前将该数据集clone一份进行返回。

至此,整个系统的开发初步完成。

  web站点部署

得益于springboot的优良特性;其将tomcat服务器整合到自身架构里,对于web项目,springboot基于maven打成jar包而后可以以普通java程序执行。因此,本系统将整个web项目打成jar包执行。此处在jar生成中会遇到一些问题,首先是在生成jar包之前需要在pom文件中加入相关插件依赖,其次在生成jar包过程中会对项目test文件进行测试,本系统在测试test文件过程中出现错误,而实际上主项目文件是没有问题的,并且test文件的出错不会对项目运行造成影响,故而在此直接忽略test测试错误(直接关闭test)。上述相关pom文件设置如下所示:

<!--compile web project to jar-->

<plugin>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-maven-plugin </artifactId>

</plugin>

<plugin>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-maven-plugin</artifactId>

<configuration>

<!--self entrance class of springboot-->                    

<mainClass>

springBootWithEcharts.BaseApplication

</mainClass>

</configuration>

<executions>

    <execution>

        <goals>

            <goal>repackage</goal>

        </goals>

    </execution>

</executions>

</plugin>

<!--ignore test error-->

<plugin>

    <groupId>org.apache.maven.plugins</groupId>

    <artifactId>maven-surefire-plugin</artifactId>

    <version>2.4.2</version>

    <configuration>

        <skipTests>true</skipTests>

    </configuration>

</plugin>

此外在运行本模块web项目jar包时需要注意运行环境编码问题。若在windows控制台上运行该项目,在使用windows认GBK编码环境下会出现web页面数据获取展示为乱码的情况,这是因为本项目web页面及相关字符集编码为utf8的原因。故而可以设置windows控制台编码,或直接将jar包放在集群上运行(centOS终端字符集编码为utf8)。

经过上述开发部署,web模块作为一个单独的项目即可简单方便的运行起来并在集群spark项目运行配合下为用户提供良好的数据可视化服务。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐