流式处理是现代数据处理的主流,各种电子商务网站,搜索引擎等网站等,都需要做流式处理,比如,通过用户的点击和购买来推断出用户的兴趣爱好,后台能实时计算,这是比较重要的,给用户推荐最好的商品等,推荐更新的信息,给用户更好的服务。
Spark Streaming就是Spark Core上的一个应用程序。
Spark Streaming中数据是不断的流进来,流进来的数据不断的生成Job,不断的提交给集群去处理,要是想更清晰的看到数据流进来,更清晰的看到数据被处理,只要把Batch Interval修改的足够大,就可以看到了,对于想理解内部的运行过程,排除错误等,都是很有必要的。
下面是黑名单过滤的案例代码,用下面的代码来讲解Spark Streaming的本质和工作流程:
package
com.dt.spark.sparkstreaming
import
org.apache.spark.SparkConf
import
org.apache.spark.SparkContext
import
org.apache.spark.rdd.RDD
import
org.apache.spark.streaming.StreamingContext
import
org.apache.spark.streaming.
Seconds
/**
* 使用Scala开发集群运行的Spark在线黑名单过滤程序
* 新浪微博:http://weibo.com/ilovepains/
*/
object
OnlineBlackListFilter
{
def
main
(
args
: Array[
String
]){
/**
* 第1步:创建Spark的配置对象SparkConf,设置Spark程序的运行时的配置信息,
* 例如说通过setMaster来设置程序要链接的Spark集群的Master的URL,如果设置
* 为local,则代表Spark程序在本地运行,特别适合于机器配置条件非常差(例如
* 只有1G的内存)的初学者
*/
val
conf
=
new
SparkConf()
//创建SparkConf对象
conf
.
setAppName
(
"OnlineBlackListFilter"
)
//设置应用程序的名称,在程序运行的监控界面可以看到名称
conf
.
setMaster
(
"spark://Master:7077"
)
//此时,程序在Spark集群
val
ssc
=
new
StreamingContext(
conf
,
Seconds
(
300
))
/**
* 黑名单数据准备,实际上黑名单一般都是动态的,例如在 Redis或者数据库中,黑名单的生成往往有复杂的业务
* 逻辑,具体情况算法不同,但是在Spark Streaming进行处理的时候每次都能工访问完整的信息
*/
val
blackList
=
Array
((
"aaa"
,
true
),(
"bbb"
,
true
))
val
blackListRDD
=
ssc
.
sparkContext
.
parallelize
(
blackList
,
8
)
val
adsClickStream
=
ssc
.
socketTextStream
(
"Master"
,
9999
)
/**
* 此处模拟的广告点击的每条数据的格式为:time、name
* 此处map操作的结果是name、(time,name)的格式
*/
val
adsClickStreamFormatted
=
adsClickStream
.
map
{
ads
=> (
ads
.
split
(
" "
)(
1
),
ads
) }
adsClickStreamFormatted
.
transform
(
userClickRDD
=> {
//通过leftOuterJoin操作既保留了左侧用户广告点击内容的RDD的所有内容,又获得了相应点击内容是否在黑名单中
val
joinedBlackListRDD
=
userClickRDD
.
leftOuterJoin
(
blackListRDD
)
/**
* 进行filter过滤的时候,其输入元素是一个Tuple:(name,((time,name), boolean))
* 其中第一个元素是黑名单的名称,第二元素的第二个元素是进行leftOuterJoin的时候是否存在在值
* 如果存在的话,表面当前广告点击是黑名单,需要过滤掉,否则的话则是有效点击内容;
*/
val
validClicked
=
joinedBlackListRDD
.
filter
(
joinedItem
=> {
if
(
joinedItem
.
_2
.
_2
.
getOrElse
(
false
))
{
false
}
else
{
true
}
})
validClicked
.
map
(
validClick
=> {
validClick
.
_2
.
_1
})
}).
print
/**
* 计算后的有效数据一般都会写入 Kafka中,下游的计费系统会从 kafka中pull到有效数据进行计费
*/
ssc
.
start
()
ssc
.
awaitTermination
()
}
}
执行黑名单过滤代码的案例过程:
1、打包程序,放到服务器上。
2、 需要先执行nc,如果直接执行打包的程序,会报错,会报端口被拒绝的错误,因为9999端口确实未启动。
$ nc -lk 9999
3、把打包的代码提交到集群当中。
$ vim OnlineBlackListFilter
内容如下:
/usr/local/spark-1.6.1-bin-hadoop2.6/bin/spark-submit --class com.dt.spark.sparkstreaming.OnlineBlackListFilter --master spark://Master:7077 /home/SparkApps/OnlineBlackListFilter.jar
执行shell代码:
$ sh OnlineBlackListFilter.sh
我们运行完程序,看到过滤结果以后,停止程序,打开HistoryServer http://master:18080/
点击App ID
进去,打开,
会看到如下图所示的5个Job
,从实际执行的Job是1个Job,但是图中显示有5个Job,从这里可以看出Spark Streaming运行的时候自己会启动一些Job。
打开 Job Id 0 start at OnlineBlackListFilter.scala:72
打开 Job Id 1 Streaming job running receiver 0
start at OnlineBlackListFilter.scala:72
从下面的图看出1上启动Receiver。
打开 Stage Id 2 Streaming Job running receiver 0
start at OnlineBlackListFilter.scala:72
Spark Streaming启动Receiver的时候就会通过Job来启动。而且Receiver只会在一个Executor中执行,且以一个Task去接受我们的数据。Receiver接受数据和普通的Job没有任何区别,我们在一个Spark Application中可以启动很多Job,不同的Job之间可以相互配合。Spark Streaming默认情况下,内存放得下的时候不会存到磁盘当中。
打开 Job Id 2 Streaming job from [output operation 0, batch time 11:05:00] print at
OnlineBlackListFilter.scala:67
打开Stage Id 3 Streaming job from [output operation 0, batch time 11:05:00] map at
OnlineBlackListFilter.scala:46
从下面的图中可以看出虽然在一台服务器上接受数据,但是会在多台服务器上处理数据。
打开 Stage Id 5 Streaming job from [output operation 0, batch time 11:05:00] print at
OnlineBlackListFilter.scala:67
打开 Job Id 3 Streaming job from [output operation 0, batch time 11:05:00] print at
OnlineBlackListFilter.scala:67
打开 Stage Id 8 Streaming job from [output operation 0, batch time 11:05:00] print at
OnlineBlackListFilter.scala:67
打开 Job Id 4 Streaming job from [output operation 0, batch time 11:05:00] print at
OnlineBlackListFilter.scala:67
打开 Stage Id 11 Streaming job from [output operation 0, batch time 11:05:00] print at
OnlineBlackListFilter.scala:67