本文将会介绍四个Demo案例,分别是updateStateByKey算子的使用,SparkStreaming写入mysql,窗口函数的使用和黑名单的过滤,然后会将代码分享到github
github地址: https://github.com/2NaCl/spark-demo.git
目标一:updateStateByKey算子的使用
首先我们先看看官网对于这个算子的介绍:
大意是,此算子可以在保持任意状态下去更新信息,但是有两个要求:
- 所谓的状态可以是任意类型
- 要定义状态的更新,要用函数指定更新前的状态和更新后的状态
然后就来像文档所说,举一个例子套用这个算子说明其意义。
需求:统计到目前为止累积出现的单词的个数(需要保持以前的状态)
思路:
-
首先配置好sparkConf和StreamingContext,每五秒统计一次
-
利用socket通信,监控一个端口
-
对端口输入进来的数据进行自定义的过滤
-
然后使用updateStateByKey,将每次输入的数据按照旧值进行更新
调用的这个函数,我们参照一个官网的案例
意思就是说,要维护文本数据流中看到的每个单词的运行计数(也就现在这个状态),在这里,运行状态是一个int类型的,它会 自动给输入的字段进行更新函数的调用,传入进来的Seq举个例子就是(a,1)(b,1)这样的,他会给你记录下来,然后你要怎么做看你,但是不管你的操作如何,他都会执行runningCount,把旧的序列进行聚合操作,但是为什么新数据是Seq属性,老数据是Option呢,因为也许我们这里会产生新的值,那么老的值肯定就不存在,是null,所以要用Option,这个Demo为了演示方便,所以直接用的.sum
我们可以看到,老数据执行的是getOrElse(0),这就是因为我们不确定是否以前是否是存在的。
- 注意:因为我们使用的算子是stateful类型的,所以要用streamingContext声明一个checkpoint,这个我不知道为啥,反正官网这么写的
代码在最上方github里,有兴趣可以自己clone一下跑着玩玩
目标二:计算累积出现的单词个数写入mysql
代码还用上面的,这个目标二单纯只做写入操作,上面说的很详细了,可以好好理解的,不行可以看官网。
这里会用到一个Foreach操作,也是先来看文档
大意是说:这个算子作用于一个函数通过流产生的每一个RDD,然后推送到外部系统,基本都用于流操作中。
然后说几点注意的地方,也是官网提示的
大意就是:不要在Spark驱动程序中创建连接对象,然后尝试在Spark工作程序中使用它来保存RDD中的记录,不然会出问题,也就是这样:
这肯定是不对的,会产生序列化错误的,因为工作的地方和连接产生的地方不同,正确的方法是在worker创建连接,但是这又会产生另一个问题:
我们确实做到了在工作的地方建立连接,但是相对的又会产生很多资源消耗,所以我们就要使用 rdd.foreachPartition用一个连接,推送一个分区内所有的记录
但是如果我们有的时候需要分开推送,有的推送有的不推送怎么办呢,那么我们就可以使用到连接池技术:
然后来操作一下我们的程序:
需求:将统计结果写入mysql数据库
思路:
- 首先创建一张表
-
建立mysql的连接
-
使用算子
- 运行输入
目标三:窗口函数的使用
先来说一下什么是窗口函数,
window
:定时的进行一个时间段内的数据处理
首先还是先来看官方文档
大意是 在固定时间范围的情况下,捕捉该范围的数据进行操作,并且操作之后会滑动一定的时间单位,不断的去进行运算,所以我们使用窗口函数需要制定两个参数:
- 窗口长度 - 窗口的持续时间(图中的3)。
- 滑动间隔 - 执行窗口操作的间隔(图中的2)。
而且既然我们滑动了,那么就不能有重复计算,所以滑动的记录要求一定得是窗口长度的倍数
举一个官网的例子:
这就代表每10秒执行前30秒的data,并且除此之外还有一些其他的api,但是所含参数必须要有上面提到的那两个
目标四:黑名单过滤
我们有两个步骤,第一是使用transform算子,然后就是使用SparkStreaming整合RDD进行操作
思路:
-
常规的进行配置
-
在这里我们手动定义黑名单的姓名,真实环境可以从数据库获取
-
然后我们将黑名单的List转换成一个map形式的RDD,姓名为key,是否为黑名单定义为true
-
还是继续向端口注入消息
-
最关键的一步,我们需要的是将端口注入的(k,v)形式的消息,拿出value值也就是姓名,拿去判断,因为输入进来的之间有逗号,所以用scala使用.split(",")分开然后取出第二个值,取出输入的姓名之后就要拿去和blackRDD去对比了,所以需要filter操作,这个时候,我们过滤之前的值应该是(<20180808,zs>,<true>),过滤之后的目的就是,只删选出第二个值为true的姓名,然后将姓名重新组合成map
然后运行
输入
控制台打印: