一.环境准备

1.pom文件

<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.12</artifactId>
<version>3.0.0</version>
</dependency> <dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.12</artifactId>
<version>3.0.0</version>
</dependency> <dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-10_2.12</artifactId>
<version>3.0.0</version>
</dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.27</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.1</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>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.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>
</plugins>
</build>

2.bean

import java.text.SimpleDateFormat
import java.util.Date
//数据格式:1597148289569,华北,北京,102,4,2020-08-11,11:12
case class AdsInfo(ts: Long,
area: String,
city: String,
userId: String,
adsId: String,
var dayString: String = null, // yyyy-MM-dd
var hmString: String = null) { // hh:mm val date = new Date(ts)
dayString = new SimpleDateFormat("yyyy-MM-dd").format(date)
hmString = new SimpleDateFormat("HH:mm").format(date)
}

3.工具类

JDBCUtils

object JDBCUtil {

    // 创建连接池对象
var dataSource:DataSource = init() // 连接池的初始化
def init():DataSource = { val paramMap = new java.util.HashMap[String, String]()
paramMap.put("driverClassName", PropertiesUtil.getValue("jdbc.driver.name"))
paramMap.put("url", PropertiesUtil.getValue("jdbc.url"))
paramMap.put("username", PropertiesUtil.getValue("jdbc.user"))
paramMap.put("password", PropertiesUtil.getValue("jdbc.password"))
paramMap.put("maxActive", PropertiesUtil.getValue("jdbc.datasource.size")) // 使用Druid连接池对象
DruidDataSourceFactory.createDataSource(paramMap)
} // 从连接池中获取连接对象
def getConnection(): Connection = {
dataSource.getConnection
} def main(args: Array[String]): Unit = { println(getConnection()) }
}

Properties工具类

/**
* project.properties文件
*/
#jdbc配置
jdbc.datasource.size=10
jdbc.url=jdbc:mysql://hadoop102:3306/steamingproject?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
jdbc.user=root
jdbc.password=root
jdbc.driver.name=com.mysql.jdbc.Driver # Kafka配置
kafka.broker.list=hadoop102:9092,hadoop103:9092,hadoop104:9092
kafka.topic=mytest
kafka.group.id=cg1

import java.util.ResourceBundle
/**
* Properties文件工具类
*/
object PropertiesUtil { // 绑定配置文件
// ResourceBundle专门用于读取配置文件,所以读取时,不需要增加扩展名
// 国际化 = I18N => Properties
val summer: ResourceBundle = ResourceBundle.getBundle("project") def getValue( key : String ): String = {
summer.getString(key)
} def main(args: Array[String]): Unit = { println(getValue("jdbc.user")) }
}

3.创建BaseApp

/**
* @description: 基础类
* @author: HaoWu
* @create: 2020年08月11日
*/
abstract class BaseApp {
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("myAPP")
val ssc: StreamingContext = new StreamingContext(conf, Seconds(3))
//设置消费kafka的参数,可以参考kafka.consumer.ConsumerConfig类中配置说明
val kafkaParams: Map[String, Object] = Map[String, Object](
"bootstrap.servers" -> "hadoop102:9092,hadoop103:9092,hadoop104:9092", //zookeeper的host,port
"group.id" -> "g3", //消费者组
"enable.auto.commit" -> "true", //是否自动提交
"auto.commit.interval.ms" -> "500", //500ms自动提交offset
"key.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer",
"value.deserializer" -> "org.apache.kafka.common.serialization.StringDeserializer",
"auto.offset.reset" -> "earliest" //第一次运行,从最初始偏移量开始消费数据
) //消费kafka的mytest主题生成DStream
val ds: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String, String](
ssc,
LocationStrategies.PreferConsistent,
//订阅主题
ConsumerStrategies.Subscribe[String, String](List("mytest"),
kafkaParams)) /**
* 将输入流InputDStream[ConsumerRecord[String, String]]=>stream[对象]
* @param ds
* @return
*/
def getAllBeans(ds: InputDStream[ConsumerRecord[String, String]]): DStream[AdsInfo] = {
val result: DStream[AdsInfo] = ds.map(
record => {
val arr: Array[String] = record.value().split(",")
AdsInfo(arr(0).toLong, arr(1), arr(2), arr(3), arr(4))
}
)
result
} /**
* 处理逻辑
* @param opt
*/
def runApp(opt: => Unit): Unit = {
try {
//处理逻辑
opt
//执行程序
ssc.start()
ssc.awaitTermination()
} catch {
case e: Exception => e.getMessage
}
} }

需求一:动态添加黑名单

实现实时的动态黑名单机制:将每天对某个广告点击超过 100 次的用户拉黑。

注:黑名单保存到MySQL中。

思路分析

1)读取Kafka数据之后,并对MySQL中存储的黑名单数据做校验;

2)校验通过则对给用户点击广告次数累加一并存入MySQL;

3)在存入MySQL之后对数据做校验,如果单日超过100次则将该用户加入黑名单。

准备工作

1)存放黑名单用户的表
CREATE TABLE black_list (userid CHAR(2) PRIMARY KEY);
2)存放单日各用户点击每个广告的次数
CREATE TABLE user_ad_count (
dt date,
userid CHAR (2),
adid CHAR (2),
count BIGINT,
PRIMARY KEY (dt, userid, adid)
);
/**
* @description: 需求一:动态添加黑名单
* 说明:实现实时的动态黑名单机制:将每天对某个广告点击超过 100 次的用户拉黑
* (用户,广告id,时间,次数)
* 注:黑名单保存到MySQL中
* @author: HaoWu
* @create: 2020年08月12日
*/
object ProjectDemo_1 extends BaseApp {
def main(args: Array[String]): Unit = {
runApp {
val asdInfo: DStream[AdsInfo] = getAllBeans(ds) /**
* 校验数据是否在黑名单中
*/
def isBlackList(userid: String, connection: Connection): Boolean = {
var flag: Boolean = true
val sql =
"""
|select * from black_list where userid = ?
|""".stripMargin
val ps: PreparedStatement = connection.prepareStatement(sql)
ps.setString(1, userid)
val result: ResultSet = ps.executeQuery()
if (result != null) {
flag = false
}
flag
} //1.聚合当前批次数据((timestamp,userid,adsid),count)
val countDS: DStream[((String, String, String), Long)] = asdInfo.map {
//((2020-08-11,102,1),1)
case adsInfo: AdsInfo => ((adsInfo.dayString, adsInfo.userId, adsInfo.adsId), 1L)
}.reduceByKey(_ + _) countDS.foreachRDD(
rdd => rdd.foreachPartition {
iter => {
//2.向mysql插入数据,准备插入sql和连接
val connection: Connection = JDBCUtil.getConnection()
val sql =
"""
|insert into user_ad_count values(?,?,?,?)
|ON DUPLICATE KEY UPDATE COUNT= count + ?
|""".stripMargin
val ps: PreparedStatement = connection.prepareStatement(sql)
//2.过滤出在名单中的数据
iter.filter {
case ((_, userid, _), _) => val falg = isBlackList(userid, connection); falg
}
//往mysql重插入更新数据
.foreach {
case ((date, userid, adsid), count) => {
ps.setString(1, date)
ps.setString(2, userid)
ps.setString(3, adsid)
ps.setLong(4, count)
ps.setLong(5, count)
ps.executeUpdate()
}
}
//关闭
ps.close() //3.插入成功之后,查询对应得userid点击广告此时是否 > 100?
val sql2 =
"""
|select userid from user_ad_count where count > 20
|""".stripMargin
val ps2: PreparedStatement = connection.prepareStatement(sql2)
val resultSet: ResultSet = ps2.executeQuery()
//封装查询出的黑名单列表
val block_list = new mutable.HashSet[String]()
while (resultSet.next()) {
val userid: String = resultSet.getString("userid")
block_list + userid
}
//关闭resulteSet,PreparedStatement
resultSet.close()
ps2.close() //4.将block_list数据依次插入黑名单表,没有就插入,有就更新
val sql3: String =
"""
|INSERT INTO black_list VALUES (?)
|ON DUPLICATE KEY UPDATE userid=?
|""".stripMargin
val ps3: PreparedStatement = connection.prepareStatement(sql3)
for (userid <- block_list) {
ps3.setString(1, userid)
ps3.setString(2, userid)
ps3.executeUpdate()
}
ps3.close()
connection.close()
}
}
)
} }
}

需求二:广告点击量实时统计

描述:实时统计每天各地区各城市各广告的点击总流量,并将其存入MySQL

步骤:①updateStateByKey有状态累加计算 ②向mysql执行插入更新操作

Mysql表


CREATE TABLE area_city_ad_count (
dt date,
area CHAR(4),
city CHAR(4),
adid CHAR(2),
count BIGINT,
PRIMARY KEY (dt,area,city,adid) --联合主键
);

代码实现

import java.sql.{Connection, PreparedStatement}
import com.spark.streaming_need.bean.AdsInfo
import com.spark.streaming_need.utils.JDBCUtil
import org.apache.spark.streaming.dstream.DStream /**
* @description: 需求二:广告点击量实时统计
* 描述:实时统计每天各地区各城市各广告的点击总流量,并将其存入MySQL
* @author: HaoWu
* @create: 2020年08月11日
*/
object ProjectDemo_2 extends BaseApp {
def main(args: Array[String]): Unit = {
runApp {
//updateStateByKey算子有状态,需要checkpoint
ssc.checkpoint("function2") //1.单个批次内对数据进行按照天维度的聚合统计
//数据格式:1597148289569,华北,北京,102,4
val DsAds: DStream[AdsInfo] = getAllBeans(ds)
val kvDS: DStream[((String, String, String, String), Int)] = DsAds.map {
case (adsInfo) => {
((adsInfo.dayString, adsInfo.area, adsInfo.city, adsInfo.adsId), 1)
}
} //2.结合MySQL数据跟当前批次数据更新原有的数据
//计算当前批次和之前的数据累加结果
val result: DStream[((String, String, String, String), Int)] = kvDS.updateStateByKey {
case (seq, opt) => {
var sum: Int = seq.sum
val value = opt.getOrElse(0)
sum += value
Some(sum)
}
}
//3.将结果写入Mysql
result.foreachRDD(
rdd => {
rdd.foreachPartition {
iter => {
//每个分区创建一个Connection连接
val connection: Connection = JDBCUtil.getConnection()
//准备sql,实现mysql的upsert操作
val sql =
"""
|insert into area_city_ad_count values (?,?,?,?,?)
|on duplicate key update count=?
|""".stripMargin
//PreparedStatement
val ps: PreparedStatement = connection.prepareStatement(sql)
//RDD分区中的每个数据都执行写出
iter.foreach {
case ((dayString, area, city, adsId), count) => {
//填充占位符
ps.setString(1, dayString)
ps.setString(2, area)
ps.setString(3, city)
ps.setString(4, adsId)
ps.setInt(5, count)
ps.setInt(6, count)
//执行写入
ps.executeUpdate()
}
}
//关闭资源
ps.close()
connection.close()
}
}
}
)
}
}
}

需求三:最近一小时广告点击量

需求说明

求最近1h的广告点击量,要求按照以下结果显示

结果展示:
1:List [15:50->10,15:51->25,15:52->30]
2:List [15:50->10,15:51->25,15:52->30]
3:List [15:50->10,15:51->25,15:52->30]

思路分析

1)开窗确定时间范围;

2)在窗口内将数据转换数据结构为((adid,hm),count);

3)按照广告id进行分组处理,组内按照时分排序。

代码实现

import org.apache.spark.streaming.{Minutes, Seconds}
import org.apache.spark.streaming.dstream.DStream /**
* @description: 需求三:最近一小时广告点击量,3秒更新一次
* @author:
* 结果展示:
* 1:List [15:50->10,15:51->25,15:52->30]
* 2:List [15:50->10,15:51->25,15:52->30]
* 3:List [15:50->10,15:51->25,15:52->30]
* @create: 2020年08月12日
*/
object ProjectDemo_3 extends BaseApp {
def main(args: Array[String]): Unit = {
//运行app
runApp {
val AdsDStream: DStream[((String, String), Int)] = getAllBeans(ds).map {
case adsInfo => ((adsInfo.adsId, adsInfo.hmString), 1)
}
val result: DStream[(String, List[(String, Int)])] = AdsDStream
//窗口内聚合
.reduceByKeyAndWindow((a: Int, b: Int) => {
a + b
}, Minutes(60), Seconds(3))
.map { case ((adsId, ahmString), count) => (adsId, (ahmString, count)) }
//按照广告id分组
.groupByKey()
//组内按时间升序
.mapValues {
case iter => iter.toList.sortBy(_._1)
}
result.print(10)
}
}
}

结果

-------------------------------------------
Time: 1597234032000 ms
-------------------------------------------
(1,List((20:01,12), (20:02,112), (20:03,98), (20:04,95), (20:05,104), (20:06,96), (20:07,13)))
(2,List((20:01,24), (20:02,97), (20:03,99), (20:04,103), (20:05,95), (20:06,105), (20:07,6)))
(3,List((20:01,30), (20:02,87), (20:03,92), (20:04,108), (20:05,117), (20:06,88), (20:07,22)))
(4,List((20:01,15), (20:02,101), (20:03,100), (20:04,99), (20:05,84), (20:06,112), (20:07,22)))
(5,List((20:01,19), (20:02,103), (20:03,111), (20:04,95), (20:05,100), (20:06,99), (20:07,10))) -------------------------------------------
Time: 1597234035000 ms
-------------------------------------------
(1,List((20:01,12), (20:02,112), (20:03,98), (20:04,95), (20:05,104), (20:06,96), (20:07,20)))
(2,List((20:01,24), (20:02,97), (20:03,99), (20:04,103), (20:05,95), (20:06,105), (20:07,13)))
(3,List((20:01,30), (20:02,87), (20:03,92), (20:04,108), (20:05,117), (20:06,88), (20:07,26)))
(4,List((20:01,15), (20:02,101), (20:03,100), (20:04,99), (20:05,84), (20:06,112), (20:07,26)))
(5,List((20:01,19), (20:02,103), (20:03,111), (20:04,95), (20:05,100), (20:06,99), (20:07,15))) -------------------------------------------
Time: 1597234038000 ms
-------------------------------------------
(1,List((20:01,12), (20:02,112), (20:03,98), (20:04,95), (20:05,104), (20:06,96), (20:07,23)))
(2,List((20:01,24), (20:02,97), (20:03,99), (20:04,103), (20:05,95), (20:06,105), (20:07,16)))
(3,List((20:01,30), (20:02,87), (20:03,92), (20:04,108), (20:05,117), (20:06,88), (20:07,34)))
(4,List((20:01,15), (20:02,101), (20:03,100), (20:04,99), (20:05,84), (20:06,112), (20:07,30)))
(5,List((20:01,19), (20:02,103), (20:03,111), (20:04,95), (20:05,100), (20:06,99), (20:07,20)))

Spark(十七)【SparkStreaming需求练习】的更多相关文章

  1. 基于spark和sparkstreaming的word2vec

    概述 Word2vec是一款由谷歌发布开源的自然语言处理算法,其目的是把words转换成vectors,从而可以用数学的方法来分析words之间的关系.Spark其该算法进行了封装,并在mllib中实 ...

  2. spark or sparkstreaming的内存泄露问题?

    关于sparkstreaming的无法正常产生数据---->到崩溃---->到数据读写极为缓慢(或块丢失?)问题 前两阶段请看我的博客:https://www.cnblogs.com/wa ...

  3. 【Spark】SparkStreaming和Kafka的整合

    文章目录 Streaming和Kafka整合 概述 使用0.8版本下Receiver DStream接收数据进行消费 步骤 一.启动Kafka集群 二.创建maven工程,导入jar包 三.创建一个k ...

  4. 【Spark】SparkStreaming与flume进行整合

    文章目录 注意事项 SparkStreaming从flume中poll数据 步骤 一.开发flume配置文件 二.启动flume 三.开发sparkStreaming代码 1.创建maven工程,导入 ...

  5. 【Spark】SparkStreaming从不同基本数据源读取数据

    文章目录 基本数据源 文件数据源 注意事项 步骤 一.创建maven工程并导包 二.在HDFS创建目录,并上传要做测试的数据 三.开发SparkStreaming代码 四.运行代码后,往HDFS文件夹 ...

  6. 【Spark】SparkStreaming的容错机制

    文章目录 检查点机制 驱动器程序容错 工作节点容错 接收器容错 处理保证 检查点机制 Metadata checkpointing -- 将定义流计算的信息存入容错的系统如HDFS. Data che ...

  7. Spark之 Spark Streaming流式处理

    SparkStreaming Spark Streaming类似于Apache Storm,用于流式数据的处理.Spark Streaming有高吞吐量和容错能力强等特点.Spark Streamin ...

  8. Spark2.1.0之初识Spark

    随着近十年互联网的迅猛发展,越来越多的人融入了互联网——利用搜索引擎查询词条或问题:社交圈子从现实搬到了Facebook.Twitter.微信等社交平台上:女孩子们现在少了逛街,多了在各大电商平台上的 ...

  9. Spark基础知识详解

    Apache Spark是一种快速通用的集群计算系统. 它提供Java,Scala,Python和R中的高级API,以及支持通用执行图的优化引擎. 它还支持一组丰富的高级工具,包括用于SQL和结构化数 ...

随机推荐

  1. fatal error: sqlite3.h: No such file or directory

    编译带有sqlite3的数据库c语言程序时,出现fatal error: sqlite3.h: No such file or directory,找不到头文件的问题.应该是是系统没有安装函数库. 在 ...

  2. 反转单词顺序列 牛客网 剑指Offer

    反转单词顺序列 牛客网 剑指Offer 题目描述 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上.同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但 ...

  3. Markdown使用方式

    区块 区块引用在段落开头使用>,后面紧跟一个空格符号 > 区块引用 > XXX > XXX 高级技巧 HTML元素 居中  <center>XXX</cent ...

  4. sql注入理解

    一.SQL注入产生的原因和危害 1.原因 SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序.而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原 ...

  5. JSON Parse error: Unexpected identifier "object";stringToAnyType报错 uni-app

    只限于uni 的局限问题,博主的报错是因为初始化某些关键数据在uni的  onLoad生命周期  和  onReady生命周期里面初始化,导致数据加载时出现个别报错的BUG JSON Parse er ...

  6. Vue的第一课

    终于学习到Vue了,美滋滋,给自己点个赞 前后端作用: 1.1vs1(一个Vue对象控制一个) <body> <div id="app"> <p> ...

  7. [python]基于windows搭建django项目

    1.首先我的环境用到的库版本如下,若下载直接pip即可 pip3 install Django==2.0.6pip3 install djangorestframework==3.8.2pip3 in ...

  8. es添加index template

    在kibana页面选择最下方的management--elasticsearch--Index Management--Index Management 选择create a template添加in ...

  9. (五)MySQL函数

    5.1  常用函数 5.2  聚合函数(常用) 函数名称 描述 COUNT() 计数 SUM() 求和 AVG() 平均值 MAX() 最大值 MIN() 最小值 ....   ....   想查询一 ...

  10. JS中innerHTML、outerHTML、innerText 、outerText、value的区别与联系?

    1.innerHTML 属性 (参考自<JavaScript高级程序设计>294页) 在读模式下,innerHTML 属性返回与调用元素的所有子节点(包括元素.注释和文本节点)对应的 HT ...