spark streaming 使用离散化流DStream作为抽象表示。DStream是随时间退役而受到的数据的序列,在内部,每个时间区间收到的数据都作为RDD存在,DStream时由这些RDD所组成的序列。DStream支持两种操作:转化操作生成新的DStream,输出操作将数据写入外部系统。除了提供RDD类似的操作外,还增加了与时间相关的新操作。
与批处理程序不同,streaming需要进行额外的配置保证不间断工作,即checkpointing机制。
#scala流式筛选
# 从sparkconf创建streamingcontext并指定1s的批处理大小
val ssc = new StreamingContext(conf,Seconds(1))
#连接到本地机器7777端口上后,使用受到的数据创建Dstream
val lines = ssc.socketTextStream("localhost",7777)
#从dstream中筛选出包含字符串error的行
val errorlines=lines.filter(_.contains("error'))
errorlines.print()
#java流式筛选
JStreamingContext jssc = new JavaStreamingContext(conf,Durations.seconds(1));
JavaDStream<String> lines=jssc.socketTextStream("localhost",7777);
JavaDstream<String> errorlines=lines.filter(new Function<String,Boolean>(){
public Boolean call(String line){
return line.contains("error");
}
});
errorLInes.spint();
以上只是设定号了要进行的计算,系统受到数据时计算就会开始。要开始接受数据,需要显式的调用streamingcontext的start方法,执行会在另一个线程中进行,需要调用awaitTermination来等待流计算完成。
spark streaming 使用微批次的建构,把流式计算当作一系列连续的小规模批处理来对待。每个时间区间内的数据生成一个RDD,DStream是一个RDD序列。
转化操作
DStream 的转化操作可以分为无状态和有状态两种
无状态转化操作中,每个批次的处理不依赖于之前批次的数据,如map、filter、reducebykey等,会分别应用到DStream中的每个RDD上,如ruducebykey会归约每个时间区间内的数据但不会归约不同区间的数据,而有状态转化操作会整合不同区间内的数据。
另外提供了transfrom,允许对DStream提供任意一个RDD到RDD的函数,这个函数会在数据流中的每个批次中被调用
有状态转化操作中,转化操作需要使用之前批次的数据或者中间结果来计算当前批次的数据,包括基于滑动窗口的转化操作和追踪状态变化的转化操作。
基于窗口的转化操作 会在一个比streamingcontext的批次间隔更长的时间范围内,通过整合多个批次的结果计算出整个窗口的结果,需提供窗口时长和滑动步长,必须为批次间隔的整数倍,窗口时长控制每次计算最近的多少个批次的数据,滑动步长默认值与批次间隔相等,用来控制对新的DSream进行计算的间隔
最常用的window,返回一个新的DStream来表示所请求的窗口操作的结果数据,其生成的DStream中的每个RDD都包含多个批次中数据
#scala
val awindows=adstream.window(seconds(30),(10))
val windowscount=awindows.count()
#java
JavaDStream<ApacheAccessLog> awind=adstream.window(Durations.seconds(30),Durations.seconds(10));
JavaDStream<Integer>windowcounts=awindow.count();
还提供更高效的窗口操作,如 reduceByWindow()和reduceBYKeyAndWindow(),接收归约函数,在整个窗口上执行。还有一种特殊形式,只考虑新进入窗口的数据和离开窗口的数据,让spark实现增量计算归约结果,这种特殊形式需要提供归约函数的一个逆函数
#scala
val ipdstream = alongdstream.map(logentry=>(logentry.getIpAddress(),1))
val ipcountdstream = ipdstream.reduceByKeyAndWindow(
{(x,y)=>x+y};
{(x,y)=>x-y},
seconds(30),
seconds(10))
#Java
class extractip extends PairFunction<ApacheAccessLog, String,Long>{
public Tuple2<String,Long> call(ApacheAccessLog entry){
return new Tuple2(entry.getIpAddress(),1l);}
}
class addlongs extends Function2<Long,Long,Long>(){
public Long call(Long v1, Long v2){return v1+v2};
}
class substrclong extends Function<Long,Long,Long>(){
public Long call(Long v1, Long v2) {return v1-v2;}
}
JavaPairDStream<String,Long> ipaddressPairDStream=accesslongDStream.mapToPair(new extractIp());
JavaPairDStream<String,Long> ipcountDstream=ipaddressPairDStream.reudceByKeyAndWIndow(
new addlongs(),new subtractlongs(), Durations.seconds(30),Durations.seconds(10));
countBywindow() 返回每个窗口中元素个数的DStream,countByValueAndWindow()返回窗口中每个值的个数
updateStateByKey转化操作
提供对一个状态变量的访问,用于键值对形式的DStream。给定一个由(键,事件)对构成的DStream,并传递一个指定如何根据新的时间更新每个键对应状态的函数,可以构建出新的DStream,其内部数据为(键,状态)对
#scala使用updatestatebykey()跟踪日志消息中各HTTP响应代码的技术,键时响应代码,状态是代表各响应代码计数的证书,事件则是页面访问
def updateRunningSum(values:Seq[Long],state:Option[Long])={
Some(state.getOrElse(0L)+VALUES.SIZE)
}
val responseCodeDStream = accesslongDStream.map(log=>(log.getResponseCode(),1L))
val responseCodeCountDStream=responseCodeDStream.updateStateByKey(updateRunningSUm_)
#java
class UpdataRunningSUm implements Function2<List<LOng>,Optional<Long>,Optional<Long>>{
public Optional<Long> call(List<LOng> nums, Optional<Long> current){
long sum= current.or(0L);
return Optional.of(sum +nums.size());
}};
JavaPairDStream<Integer,Long> responseCodeCountDStream= accesslongsDStream.maoToPaie(new PairFunction<ApacheAccessLog, Integer,Long>(){
public Tuple2<Integer,Long>call(ApacheAccessLog log){
return new Tuple2( log.getResponseCode(),1L);
}}).UpdateStateByKey(new UpdateRunningSum());
输出操作
指定了对流数据经转化操作得到的数据所要执行的操作,如把结果推入外部数据库或输出到屏幕上,与RDD的惰性求值类似,如果一个DStream及其派生出的DStream没有被执行输出操作,那这些DStream就都不会被求值
print、save等
#java中将DStream保存为sequencefile
JavaPairDStream<Text,LongWritable> writableDStream = ipDStream.mapToPair(
new PairFunction<TUple2<String,Long>,Text,LongWritable>(){
public Tuple2<Text,LongWritable>call(Tuple2<String,Long>e){
return new Tuple2(new Text(e.-1()),new LongWritable(e._2()))
}
});
class OutFormat extends SequenceFileOutputFormat<Text,LongWritable>{};
writableDStream.saveAsHadoopFile("outputDir","txt',Text.calss,LongWritable.class,OutFormat.class);
通用的输出操作 foreachRDD(),用来对DStream中的RDD运行任意计算,在fireachRDD中可以使用我们在spark中实现的所有行动操作。
#scala 使用foreachRDD将数据存储到外部系统中
ipaddressrequenstcount.foreachRDD{RDD=>
rdd.foreachPartition{partition =>
//打开到存储系统的连接
paitition.foreach{item =>
//使用连接把item存到系统中
}
关闭连接
}
}
输入源
文件流
val longData = ssc.textFileStream(longDirectory)
JavaDStram<String> logData=jssc.textFileStream(logsDirectory);
除了文本数据,也可以读如任意Hadoop输入格式,只需将key、value、InputFormat类提供给spark streaming即可
ssc.fileStream[LongWritable,IntWritable,SequenceFileInputFormat[LOngWritable,IntWritable]](inputDirectory).map{
case(x,y)=>(x.get(),y.get())}