使用爬虫等获取实时数据+Flume+Kafka+Spark Streaming+mysql+Echarts实现数据动态实时采集、分析、展示

【获取完整源码关注公众号:靠谱杨阅读人生 回复kafka获取下载链接】

主要工作流程如下所示:

模拟随机数据,把数据实时传输到Linux虚拟机文件中。

使用Flume实时监控该文件,如果发现文件内容变动则进行处理,将数据抓取并传递到Kafka消息队列中。

之后使用Spark Streaming 实时处理Kafka中的数据,并写入Windows本机mysql数据库中,之后python读取mysql数据库中的数据并基于Echart图表对数据进行实时动态展示。


启动hadoop集群 myhadoop.sh start 【脚本参考 https://www.cnblogs.com/rainbow-1/p/16774523.html】

启动zookeeper集群 myzk.sh start 【脚本参考 https://www.cnblogs.com/rainbow-1/p/15319226.html】

启动kafka集群 kf.sh start 【脚本参考 https://www.cnblogs.com/rainbow-1/p/16015749.html】

一、实时数据的模拟

案例简化了第一步的流程,使用模拟数据进行测试,代码如下:

import datetime
import random
import time import paramiko hostname = "hadoop102"
port = 22
username = "root"
password = "000429"
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname, port, username, password, compress=True)
sftp_client = client.open_sftp()
# try:
# for line in remote_file:
# print(line)
# finally:
# remote_file.close()
#获取系统时间
num1=3000
for i in range(1000):
remote_file = sftp_client.open("/opt/module/data/test1.csv", 'a') # 文件路径
time1 = datetime.datetime.now()
time1_str = datetime.datetime.strftime(time1, '%Y-%m-%d %H:%M:%S')
print("当前时间: " + time1_str)
time.sleep(random.randint(1,3))
num1_str=str(num1+random.randint(-1300,1700))
print("当前随机数: "+num1_str)
remote_file.write(time1_str+","+num1_str+"\n")
remote_file.close()
  • 主要过程
  1. 在/opt/module/data/路径下建立test1.csv文件

  2. 代码实现远程连接虚拟机hadoop102并以root用户身份登录,打开需要上传的文件目录。

  3. 使用一个for循环间隔随机1到3秒向文件中写入一些数据。


二、Flume实时监控文件

  1. 进入/opt/module/flume/job路径编辑配置文件信息(myflume.conf)

    内容如下:其中指定了被监控文件的路径,Kafka服务主机地址,Kafka主题和序列化等信息

#给agent中的三个组件source、sink和channel各起一个别名,a1代表为agent起的别名
a1.sources = r1
a1.channels = c1
a1.sinks = k1 # source属性配置信息
a1.sources.r1.type = exec
#a1.sources.r1.bind = localhost
#a1.sources.r1.port = 44444
a1.sources.r1.command=tail -F /opt/module/data/test1.csv # sink属性配置信息
a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
a1.sinks.k1.kafka.bootstrap.servers:hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.sinks.k1.kafka.topic=first
a1.sinks.k1.serializer.class=kafka.serializer.StringEncoer #channel属性配置信息
#内存模式
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
#传输参数设置
a1.channels.c1.transactionCapacity=100 #绑定source和sink到channel上
a1.sources.r1.channels=c1
a1.sinks.k1.channel=c1
  1. 在/opt/module/flume 路径下开启Flume,此时Flume开始监控目标文件(job/myflume.conf)
bin/flume-ng agent -c conf/ -n a1 -f job/myflume.conf -Dflume.root.logger=INFO,console

三、使用Spark Streaming完成数据计算

  1. 新建一个名为first的消费主题(topic)

    bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --create --partitions 1 --replication-factor 1 --topic first1
  2. 新建Maven项目,编写代码,Kafka的topic主题的消费者

    pom.xml配置如下:注意此处各个资源的版本号一定要与本机(IDEA编译器)的Scala版本一致,博主为Scala 2.12.11

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.reliable.ycw</groupId>
    <artifactId>spark-test</artifactId>
    <version>1.0-SNAPSHOT</version> <dependencies>
    <!-- https://mvnrepository.com/artifact/org.apache.spark/spark-network-common -->
    <!--<dependency>-->
    <!--<groupId>org.apache.spark</groupId>-->
    <!--<artifactId>spark-network-common_2.12</artifactId>-->
    <!--<version>3.0.0</version>-->
    <!--</dependency>-->
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.18</version>
    </dependency>
    <dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-core_2.12</artifactId>
    <version>3.0.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.spark/spark-streaming -->
    <dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-streaming_2.12</artifactId>
    <version>3.0.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.spark/spark-streaming-kafka-0-10 -->
    <dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-streaming-kafka-0-10_2.12</artifactId>
    <version>3.0.0</version>
    </dependency>
    <dependency>
    <groupId>org.scala-lang</groupId>
    <artifactId>scala-library</artifactId>
    <version>2.12.11</version>
    </dependency>
    <dependency>
    <groupId>org.scala-lang</groupId>
    <artifactId>scala-compiler</artifactId>
    <version>2.12.11</version>
    </dependency>
    <dependency>
    <groupId>org.scala-lang</groupId>
    <artifactId>scala-reflect</artifactId>
    <version>2.12.11</version>
    </dependency>
    </dependencies>
    <build>
    <plugins>
    <!-- 该插件用于将 Scala 代码编译成 class 文件 -->
    <plugin>
    <groupId>net.alchim31.maven</groupId>
    <artifactId>scala-maven-plugin</artifactId>
    <version>3.2.2</version>
    <executions>
    <execution>
    <!-- 声明绑定到 maven 的 compile 阶段 -->
    <goals>
    <goal>testCompile</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.1.0</version>
    <configuration>
    <descriptorRefs>
    <descriptorRef>jar-with-dependencies</descriptorRef>
    </descriptorRefs>
    </configuration>
    <executions>
    <execution>
    <id>make-assembly</id>
    <phase>package</phase>
    <goals>
    <goal>single</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
    <source>8</source>
    <target>8</target>
    </configuration>
    </plugin>
    </plugins>
    </build>
    </project>

    消费者类代码如下:

    import org.apache.kafka.common.serialization.StringDeserializer
    import org.apache.spark.SparkConf
    import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe
    import org.apache.spark.streaming.kafka010.KafkaUtils
    import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
    import java.sql.DriverManager
    import java.text.SimpleDateFormat
    import java.util.Date /**
    * 主要是计算X秒内数据条数的变化
    * 比如5秒内进来4条数据
    */ import org.apache.spark.streaming.{Seconds, StreamingContext}
    /** Utility functions for Spark Streaming examples.*/
    object StreamingExamples extends App{
    /** Set reasonable logging levels for streaming if the user has not configured log4j.*/
    // def setStreamingLogLevels() {
    // val log4jInitialized = Logger.getRootLogger.getAllAppenders.hasMoreElements
    // if (!log4jInitialized) {
    // // We first log Appsomething to initialize Spark's default logging, then we override the
    // // logging level.
    // logInfo("Setting log level to [WARN] for streaming example." +
    // " To override add a custom log4j.properties to the classpath.")
    // Logger.getRootLogger.setLevel(Level.WARN)
    // }
    // }
    val conf=new SparkConf().setMaster("local").setAppName("jm")
    .set("spark.streaming.kafka.MaxRatePerPartition","3")
    .set("spark.local.dir","./tmp")
    .set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
    //创建上下文,5s为批处理间隔
    val ssc = new StreamingContext(conf,Seconds(5)) //配置kafka参数,根据broker和topic创建连接Kafka 直接连接 direct kafka
    val KafkaParams = Map[String,Object](
    //brokers地址
    "bootstrap.servers"->"hadoop102:9092,hadoop103:9092,hadoop104:9092",
    //序列化类型
    "key.deserializer"->classOf[StringDeserializer],
    "value.deserializer" -> classOf[StringDeserializer],
    "group.id" -> "MyGroupId",
    //设置手动提交消费者offset
    "enable.auto.commit" -> (false: java.lang.Boolean)//默认是true
    ) //获取KafkaDStream
    val kafkaDirectStream = KafkaUtils.createDirectStream[String,String](ssc,
    PreferConsistent,Subscribe[String,String](List("first"),KafkaParams))
    kafkaDirectStream.print()
    var num=kafkaDirectStream.count()
    var num_1=""
    num foreachRDD (x => {
    //var res=x.map(line=>line.split(","))
    val connection = getCon()
    var time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date).toString
    var sql = "insert into content_num values('" + time + "'," + x.collect()(0) + ")"
    connection.createStatement().execute(sql)
    connection.close()
    })
    // print("sdfasdf")
    // print(num_1)
    //根据得到的kafak信息,切分得到用户电话DStream
    // val nameAddrStream = kafkaDirectStream.map(_.value()).filter(record=>{
    // val tokens: Array[String] = record.split(",")
    // tokens(1).toInt==0
    // })
    //
    // nameAddrStream.print()
    // .map(record=>{
    // val tokens = record.split("\t")
    // (tokens(0),tokens(1))
    // })
    //
    //
    // val namePhoneStream = kafkaDirectStream.map(_.value()).filter(
    // record=>{
    // val tokens = record.split("\t")
    // tokens(2).toInt == 1
    // }
    // ).map(record=>{
    // val tokens = record.split("\t")
    // (tokens(0),tokens(1))
    // })
    //
    // //以用户名为key,将地址电话配对在一起,并产生固定格式的地址电话信息
    // val nameAddrPhoneStream = nameAddrStream.join(namePhoneStream).map(
    // record=>{
    // s"姓名:${record._1},地址:${record._2._1},邮编:${record._2._2}"
    // }
    // )
    // //打印输出
    // nameAddrPhoneStream.print()
    //开始计算
    ssc.start()
    ssc.awaitTermination()
    def getCon()={
    Class.forName("com.mysql.cj.jdbc.Driver")
    DriverManager.getConnection("jdbc:mysql://localhost:3306/spark?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8","root","reliable")
    }
    }

    这段代码指定了虚拟机中Kafka的主题信息,并从中定时获取(博主设置的为5秒)期间变化的信息量,完成计算后把本机的时间和信息变化量存储到本地Mysql数据库中【库spark 表content_num 字段 type num】

    • 注意指定时区和编码

      jdbc:mysql://localhost:3306/spark?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8

四、可视化

使用Echarts平滑折线图完成数据的展示

  1. 后台读取mysql的数据【spark_sql.py】
import pymysql
def get_conn():
"""
获取连接和游标
:return:
"""
conn = pymysql.connect(host="127.0.0.1",
user="root",
password="reliable",
db="spark",
charset="utf8")
cursor = conn.cursor()
return conn, cursor def close_conn(conn, cursor):
"""
关闭连接和游标
:param conn:
:param cursor:
:return:
"""
if cursor:
cursor.close()
if conn:
conn.close() # query
def query(sql, *args):
"""
通用封装查询
:param sql:
:param args:
:return:返回查询结果 ((),())
"""
conn, cursor = get_conn()
print(sql)
cursor.execute(sql)
res = cursor.fetchall()
close_conn(conn, cursor)
return res def dynamic_bar():
# 获取数据库连接
conn, cursor = get_conn()
if (conn != None):
print("数据库连接成功!")
typenumsql = "select * from content_num order by num desc limit 11;"
detail_sql = ""
res_title = query(typenumsql)
type_num = [] # 存储类别+数量
for item1 in res_title:
type_num.append(item1)
return type_num
  1. 路由获取后台数据
@app.route('/dynamic_bar')
def dynamic_bar():
res_list=spark_sql.dynamic_bar()
my_list=[]
list_0=[]
list_1=[]
for item in res_list:
list_0.append(item[0])
list_1.append(item[1])
my_list.append(list_0)
my_list.append(list_1)
return {"data":my_list}
  1. 前台绘制折线图 line.html
<!DOCTYPE html>
<html style="height: 100%">
<head>
<meta charset="utf-8">
</head>
<body style="height: 100%; margin: 0">
<div id="container" style="height: 100%"></div>
<script type="text/javascript" src="../static/js/echarts.min.js"></script>
<script src="../static/js/jquery-3.3.1.min.js"></script>
</body>
</html>
<script>
var dom = document.getElementById("container");
var myChart = echarts.init(dom);
var app = {};
var option;
</script> <script type="text/javascript"> option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: [],
axisTick: {
alignWithLabel: true
}
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: 'Direct',
type: 'line',
barWidth: '60%',
data: []
}
]
}; if (option && typeof option === 'object') {
myChart.setOption(option);
}
function update(){
$.ajax({
url:"/dynamic_bar",
async:true,
success:function (data) {
option.xAxis[0].data=data.data[0]
option.series[0].data=data.data[1]
myChart.setOption(option);
},
error:function (xhr,type,errorThrown) {
alert("出现错误!")
}
})
}
setInterval("update()",100)
</script>

可视化这里需要注意的点:

  • 注意先引入echarts.min.js再引入jquery-3.3.1.min.js
  • 注意指定放置图像的div块的大小
  • 把赋值方法放在图像初始化配置代码的后面
  • 注意设置方法循环执行:setInterval("update()",100)

小结:整个流程的关键在于对实时数据的监控和展示,首先要保证数据传输的动态性,其次要保证Flume实时监控数据的变化。其中使用Kafka的目的在于当数据量足够大的时候,往往会出现数据的监控和采集速度跟不上数据的变化,所以采用Kafka消息队列机制,让其缓冲数据以实现大数据量的处理,后续需要编写Spark Streaming代码完成对消息的收集处理(存入本地mysql数据库),最后读取数据库数据并用折线图完成动态展示效果,数据库的数据是实时变动的,这就需要在读取的时候要读到最新进来的数据,这样才能看到图线的动态效果。(下图的图线会随着数据的变化动态改变!)

python爬虫等获取实时数据+Flume+Kafka+Spark Streaming+mysql+Echarts实现数据动态实时采集、分析、展示的更多相关文章

  1. 日志=>flume=>kafka=>spark streaming=>hbase

    日志=>flume=>kafka=>spark streaming=>hbase 日志部分 #coding=UTF-8 import random import time ur ...

  2. kafka + spark Streaming + Tranquility Server发送数据到druid

    花了很长时间尝试druid官网上说的Tranquility嵌入代码进行实时发送数据到druid,结果失败了,各种各样的原因造成了失败,现在还没有找到原因,在IDEA中可以跑起,放到线上就死活不行,有成 ...

  3. flume+kafka+spark streaming整合

    1.安装好flume2.安装好kafka3.安装好spark4.流程说明: 日志文件->flume->kafka->spark streaming flume输入:文件 flume输 ...

  4. demo2 Kafka+Spark Streaming+Redis实时计算整合实践 foreachRDD输出到redis

    基于Spark通用计算平台,可以很好地扩展各种计算类型的应用,尤其是Spark提供了内建的计算库支持,像Spark Streaming.Spark SQL.MLlib.GraphX,这些内建库都提供了 ...

  5. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(十一)定制一个arvo格式文件发送到kafka的topic,通过Structured Streaming读取kafka的数据

    将arvo格式数据发送到kafka的topic 第一步:定制avro schema: { "type": "record", "name": ...

  6. Spark Streaming的容错和数据无丢失机制

    spark是迭代式的内存计算框架,具有很好的高可用性.sparkStreaming作为其模块之一,常被用于进行实时的流式计算.实时的流式处理系统必须是7*24运行的,同时可以从各种各样的系统错误中恢复 ...

  7. Apache Kafka + Spark Streaming Integration

    1.目标 为了构建实时应用程序,Apache Kafka  - Spark Streaming Integration是最佳组合.因此,在本文中,我们将详细了解Kafka中Spark Streamin ...

  8. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(九)安装kafka_2.11-1.1.0

    如何搭建配置centos虚拟机请参考<Kafka:ZK+Kafka+Spark Streaming集群环境搭建(一)VMW安装四台CentOS,并实现本机与它们能交互,虚拟机内部实现可以上网.& ...

  9. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(十)安装hadoop2.9.0搭建HA

    如何搭建配置centos虚拟机请参考<Kafka:ZK+Kafka+Spark Streaming集群环境搭建(一)VMW安装四台CentOS,并实现本机与它们能交互,虚拟机内部实现可以上网.& ...

  10. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(二)安装hadoop2.9.0

    如何搭建配置centos虚拟机请参考<Kafka:ZK+Kafka+Spark Streaming集群环境搭建(一)VMW安装四台CentOS,并实现本机与它们能交互,虚拟机内部实现可以上网.& ...

随机推荐

  1. Java并发编程实例--10.使用线程组

    并发API提供的一个有趣功能是可以将多个线程组成一个组. 这样我们就能将这一组线程看做一个单元并且提供改组内线程对象的读取操作.例如 你有一些线程在执行同样的任务并且你想控制他们,不考虑有多少个线程仍 ...

  2. HTML前置知识

    1.概念 HTML:超文本标记语言 (英语:Hypertext Markup Language,简称:HTML ) 创建网页的标准标记语言 后缀:html,htm(两者没有区别) html语法对大小写 ...

  3. ADVMP 三代壳(vmp加固)原理分析(执行流程)

    由于在加壳时插入了System.loadLibrary("advmp");,看一下JNI_OnLoad JNIEXPORT jint JNICALL JNI_OnLoad(Java ...

  4. CentOS7安装Chrome及驱动

    目录 安装Chrome 更新Chrome 安装Chrome驱动程序 更新Chrome驱动程序 环境:CentOS Linux release 7.4.1708 (Core) 安装Chrome 下载安装 ...

  5. Unity学习笔记--数据持久化XML文件(1)

    XML相关 Xml是可拓展标记语言,一种文件格式.我们使用xml来完成对数据持久化的存储.等待我们有一程序运行结束之后,将内存中的数据进行保存,(保存在硬盘/服务器)实现对数据的持久化存储. xml文 ...

  6. 零难度指南:手把手教你如何通过在线Excel实现资产负债表

    前言 作为财务分析中的三大报表之一,资产负债表的作用是展示一个企业在特定时间点上的财务状况.今天小编就为大家介绍一下如何使用葡萄城公司的纯前端在线表格控件SpreadJS实现一个资产负债表. 环境准备 ...

  7. 亲测可用,ChatGPT 对话技巧

      "Linux终端" "我希望你充当一个 linux 终端.我会输入命令,你会回复终端应该显示的内容.我希望你只回复一个唯一代码块内的终端输出,没有别的.不要写解释.除 ...

  8. 影刀rpa:关于if单条件切换到多条件时的不便之处

    现有需求,只判断一个条件是否满足即可,但随着后续业务开发,这里得if就需要判断多个条件,此时要是想将if单条件改为多条件的话,就得先拉一个if多条件的指令,然后再将if单条件中的语句全部移动到if多条 ...

  9. FeignClient 报错: A bean with that name has already been defined and overriding is disabled.

    1. 错误信息 *************************** APPLICATION FAILED TO START *************************** Descript ...

  10. P2251 质量检测(分块线段树RMQ单调队列)

    P2251 质量检测 正解应该是ST表和单调队列,不过对于这道题来说只有查询没有修改,这里我还是想用线段树和分块来写,不得不说分块是真好,优雅的暴力 线段树版本: #include <bits/ ...