1.首先要解决版本问题,先看自己的spark的版本,这一步及其重要,scala版本必须相同 我的scala版本是 2.11.12
2.在你的idea下面导入相同的scala版本,步骤放上
3.引入jar包 ,现在我的kafka的版本是 2.11-2.00的版本
所以 streaming-kafka-0-10_2.10 这个包一定要2.10
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile (group: 'org.apache.spark', name: 'spark-core_2.10', version:'2.2.0')
compile (group: 'org.apache.spark', name: 'spark-streaming_2.10', version:'2.2.0')
compile group: 'org.apache.spark', name: 'spark-streaming-kafka-0-10_2.10', version: '2.2.0'
}
4.完成上面的环境搭建也就完成重要的一步了,下面开始一个简单的案例,我们先到cmd下面创建主题,启动一个生产者,不了解kafka的命令的可以查看这个网站kafka命令点击这里,如何我们新建一个项目
1.先是声明一个构造器
Logger.getLogger("org").setLevel(Level.ERROR) //配置日志
val conf = new SparkConf().setMaster("local[*]").setAppName("NetworkWordCount")//设置spark运行环境和运行线程数,和运行名
val ssc = new StreamingContext(conf, Seconds(2))//实时流信息 每隔两秒到卡夫卡里面去poll拉取信息```
2.这里其实可以把这里看成是一个kafka的消费端,所以要反序列化消息,指定消费者的状态
//当前是consumer端,要反序列化消息
val kafkaParams = Map[String, Object](
"bootstrap.servers" -> "localhost:9092,localhost:9093,localhost:9094",
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> "streaming74", //消费者组编号
"auto.offset.reset" -> "latest", //消息从哪里开始读取 latest 从头
"enable.auto.commit" -> (true: java.lang.Boolean) //消息的位移提交方式
)
//要订阅的主题
val topics = Array("topic74streaming1")
这里简单介绍一下消费组的原理,其实可以将消费组看成是一个消费者,这个消费者回读取分区上面的所有的数据,两个消费组直接数据是互斥的,但是两消费者组直接是数据共享的
3.这一步 就是要创建DStream 可以将DStream看成是一个个的RDD,就可以利用scala里面的函数来进行简单计算了
给一个git帐号,里面还有很多的案例可供参考点击这里可以查看更多案例和完整的项目
//创建DStream
val stream = KafkaUtils.createDirectStream[String, String](
ssc,
PreferConsistent,
Subscribe[String, String](topics, kafkaParams) // SubscribePattern:主题名由正则表示 , Subscribe:主题名固定 Assign:固定分区
)
// ConsumerRecord 从Kafka里面读到的数据
val lines:DStream[String]=stream.map(record => ( record.value) )
//将数据进行展平
val words:DStream[String]=lines.flatMap( _.split(" "))
//将每一个单词映射成 (word,1)的格式
val wordAndOne:DStream[(String,Int)]=words.map( (_,1) )
//根据键相同的值来进行归约
val reduced:DStream[ (String,Int) ]=wordAndOne.reduceByKey( _+_)
reduced.print()//打印输出
ssc.start()//开始实时流监控
ssc.awaitTermination()//线程等待
全部代码
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.log4j.{Level, Logger}
import org.apache.spark.{HashPartitioner, SparkConf}
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe
import org.apache.spark.streaming.kafka010.KafkaUtils
import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
import org.apache.spark.streaming.{Seconds, StreamingContext}
object kafkaTest_status{
def main(args: Array[String]): Unit = {
Logger.getLogger("org").setLevel(Level.ERROR) //配置日志
val conf = new SparkConf().setMaster("local[*]").setAppName("NetworkWordCount")
val ssc = new StreamingContext(conf, Seconds(2))
// ssc.checkpoint("./chpoint") //以后是一个hdfs 对于窗口和有状态的操作必须checkpoint,通过StreamingContext的checkpoint来指定目录,
//当前是consumer端,要反序列化消息
val kafkaParams = Map[String, Object](
"bootstrap.servers" -> "localhost:9092,localhost:9093,localhost:9094",
"key.deserializer" -> classOf[StringDeserializer],
"value.deserializer" -> classOf[StringDeserializer],
"group.id" -> "streaming74", //消费者组编号
"auto.offset.reset" -> "latest", //消息从哪里开始读取 latest 从头
"enable.auto.commit" -> (true: java.lang.Boolean) //消息的位移提交方式
)
//要订阅的主题
val topics = Array("topic74streaming1")
//创建DStream
val stream = KafkaUtils.createDirectStream[String, String](
ssc,
PreferConsistent,
Subscribe[String, String](topics, kafkaParams) // SubscribePattern:主题名由正则表示 , Subscribe:主题名固定 Assign:固定分区
)
// ConsumerRecord
val lines:DStream[String]=stream.map(record => ( record.value) )
val words:DStream[String]=lines.flatMap( _.split(" "))
val wordAndOne:DStream[(String,Int)]=words.map( (_,1) )
//val reduced:DStream[ (String,Int) ]=wordAndOne.reduceByKey( _+_)
val reduced=wordAndOne.updateStateByKey( updateFunc, new HashPartitioner( ssc.sparkContext.defaultMinPartitions ), true)
reduced.print()
ssc.start()
ssc.awaitTermination()
}
/**
* iter: 当前操作的RDD
* String: 聚合的key
* Seq[Int]: 在这个批次中此key在这个分区出现的次数集合 [1,1,1,1,1].sum()
* Option[Int]:初始值或累加值 Some None-> 模式匹配
*/
val updateFunc= ( iter:Iterator[ (String,Seq[Int] , Option[Int] ) ] ) =>{
//方案一:当成一个三元组运算
// iter.map( t=> ( t._1, t._2.sum+t._3.getOrElse(0) ) ) // -> { word:总次数}
//方案二: 模式匹配来实现
iter.map{ case(x,y,z)=>( x, y.sum+z.getOrElse(0) ) }
}
}
生产端代码
package wordSetGener
import java.util.{Properties, Random}
import org.apache.kafka.clients.producer.{KafkaProducer, ProducerRecord}
import scala.io.{BufferedSource, Source}
object Product {
def main(args: Array[String]): Unit = {
val event=10
val topic = "comment"
val props = new Properties()
props.put("bootstrap.servers", "localhost:9092,localhost:9093,localhost:9094")
props.put("acks", "all") // 确认的级别 ISR
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer") //生产端用序列化
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer")
val line:BufferedSource=Source.fromFile("data/hanzi.txt")
val rd=new Random();
val li=line.mkString
//随机发送10条消息
for (i<-Range(0,event)){
val sb=new StringBuilder()
//随机生成20个数据
for (ind <- Range(0,rd.nextInt(20))){
sb+= li.charAt(rd.nextInt(li.length))
}
val userkey="User"+rd.nextInt(100)
//发送消息
val producer = new KafkaProducer[String, String](props);
val pr = new ProducerRecord[String, String](topic, userkey,sb.toString()) // 0表示消息的类型 name 地址 类型
producer.send(pr)
}```