spark--物联网设备数据分析-★★★★★

需求

在这里插入图片描述

  • ●模拟一个智能物联网系统的数据统计
  • 使用离线和实时两种方式统计如下指标:
    • 1.信号强度大于10的设备–过滤
    • 2.各种设备类型的数量–分组-聚合
    • 3.各种设备类型的平均信号强度 --分组再求平均

数据

  • 数据是json格式,SparkSQL可以直接支持json数据源
    在这里插入图片描述
{
    
    "device":"Michael","deviceType": "people","signal": 15,"time": "2018-01-02 15:20:00"}
{
    
    "device":"jack","deviceType": "people","signal": 50,"time": "2018-01-02 15:20:00"}
{
    
    "device":"lenovo","deviceType": "computer","signal": 100,"time": "2018-01-02 15:20:00"}
{
    
    "device":"thunisoft","deviceType": "soft","signal": 30,"time": "2018-01-02 15:20:00"}
{
    
    "device":"book","deviceType": "soft","signal": 18,"time": "2018-01-02 15:20:00"}
{
    
    "device":"apache","deviceType": "soft","signal": 60,"time": "2018-01-02 15:20:00"}
{
    
    "device":"spark","deviceType": "soft","signal": 20,"time": "2018-01-02 15:20:00"}
{
    
    "device":"redmi","deviceType": "phone","signal": 10,"time": "2018-01-02 15:20:00"}

代码实现-1-SparkSQL离线版

package cn.hanjiaxiaozhi.sql
​
import java.sql.Timestamp
​
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.expressions.scalalang.typed
import org.apache.spark.sql.{
    
    DataFrame, Dataset, SparkSession}/**
 * Author hanjiaxiaozhi
 * Date 2020/7/30 10:19
 * Desc 物联网设备数据分析-离线版-SparkSQL
 * {"device":"Michael","deviceType": "people","signal": 15,"time": "2018-01-02 15:20:00"}
 */
object IoTAnalysis {
    
    case class DeviceData(device: String, deviceType: String, signal: Double, time: Timestamp)def main(args: Array[String]): Unit = {
    
    
    //1.准备运行环境-SparkSQL-SparkSession
    val spark: SparkSession = SparkSession.builder().appName("IoTAnalysis").master("local[*]").getOrCreate()
    val sc: SparkContext = spark.sparkContext
    sc.setLogLevel("WARN")
    import spark.implicits._
​
    //2.加载数据-json格式的物联网设备日志信息
    //如果要使用RDD进行分析,那么读进来的就是原始的json数据,后续需要自己单独解析,麻烦!--所以可以直接使用SparkSQL的json数据源,根据简单方便
    //val jsonFileRDD: RDD[String] = sc.textFile("file:///D:\\data\\spark\\device.json")
    //使用SparkSQL的json数据源读取
    val df: DataFrame = spark.read.json("file:///D:\\data\\spark\\device.json")
    //可以使用df做后续的统计分析,也可以使用ds
    val ds: Dataset[DeviceData] = df.as[DeviceData]
    //df.show(false)
    //df.printSchema()
    //ds.show(false)
    //ds.printSchema()
    /**
     * +---------+----------+------+-------------------+
     * |device   |deviceType|signal|time               |
     * +---------+----------+------+-------------------+
     * |Michael  |people    |15    |2018-01-02 15:20:00|
     * |jack     |people    |50    |2018-01-02 15:20:00|
     * |lenovo   |computer  |100   |2018-01-02 15:20:00|
     * |thunisoft|soft      |30    |2018-01-02 15:20:00|
     * |book     |soft      |18    |2018-01-02 15:20:00|
     * |apache   |soft      |60    |2018-01-02 15:20:00|
     * |spark    |soft      |20    |2018-01-02 15:20:00|
     * |redmi    |phone     |10    |2018-01-02 15:20:00|
     * +---------+----------+------+-------------------+
     *
     * root
     * |-- device: string (nullable = true)
     * |-- deviceType: string (nullable = true)
     * |-- signal: long (nullable = true)
     * |-- time: string (nullable = true)
     *///3.处理数据-统计各个指标
    //这里可以写SQL风格也可以写DSl风格都行,因为是单表操作,所以sql风格就不再多说了,早就学过了sql单表,这里使用dsl风格来写顺便再次区分df和ds
    //需求:1.信号强度大于10的设备--过滤
    df.filter($"signal" > 10).show(false)
    df.filter('signal > 10).show(false)
    df.where($"signal" > 10).show(false)
    df.where('signal > 10).show(false)
​
    ds.filter($"signal" > 10).show(false)
    ds.filter('signal > 10).show(false)
    ds.filter(_.signal > 10).show(false) //ds有泛型,编译的使用如果写错了,有检查提示,所以开发中,很多公司愿意使用DS,当然也有公司建议使用DF(和python兼容,python里面没有泛型)
    ds.where($"signal" > 10).show(false)
    ds.where('signal > 10).show(false)
    //ds.where(_.signal>10).show(false)//报错
​
​
    //需求:2.各种设备类型的数量--分组-聚合-排序
    df.groupBy("deviceType").count().sort($"count".desc).show()
    df.groupBy("deviceType").count().sort('count.desc).show()
    /**
     * +----------+-----+
     * |deviceType|count|
     * +----------+-----+
     * |      soft|    4|
     * |    people|    2|
     * |     phone|    1|
     * |  computer|    1|
     * +----------+-----+
     */
    //ds.groupByKey(_.deviceType).count().show()
    ds.groupByKey(_.deviceType).count().sort($"count(1)".desc).show()
    /**
     * +--------+--------+
     * |   value|count(1)|
     * +--------+--------+
     * |    soft|       4|
     * |  people|       2|
     * |   phone|       1|
     * |computer|       1|
     * +--------+--------+
     *///需求:3.各种设备类型的平均信号强度 --分组再求平均
    df.groupBy("deviceType").avg("signal").show(false)
    import org.apache.spark.sql.functions._
    df.groupBy("deviceType").agg(avg("signal").as("xxx"),sum("signal")).show(false)
    //ds.groupByKey(_.deviceType).avg("signal").show(false)//报错
    import org.apache.spark.sql.expressions.scalalang.typed
    ds.groupByKey(_.deviceType).agg(typed.avg(_.signal)).toDF("deviceType","avg").show(false)
    //写法比较灵活,开发中选择自己擅长的写法
    //上面的写法掌握一种即可
​
​
    //总结:
    //1.上面的需求都很简单,可以使用各个方式去做,但是SparkSQL可以直接读取json文件,更简单
    //2.DF和DS可以相互转换,但是DF不支持泛型,但能够和Python兼容(Python没有泛型),而DS支持泛型,可以做到编译时类型检测,可以调用类型相关方法
    //3.实际开发中DF和DS都有使用,按照公司的规范和要求来,如果没有没有要求,按照自己擅长的来
    //4.不管用来一个,其实列名都是无所谓的,后续都可以根据需要设置
    //5.$"列名"和 '列名 都是将字符串转为列对象
    //6.agg表示聚合,avg表示求平均,avg是聚合函数的一种,所以可以写在agg里面, 其他的sum,max,min都是聚合函数,都可以写在agg中
    //7.SparkSQL开发经常会使用到隐式转换:上来先导入:
    //import spark.implicits._  //最多
    //import org.apache.spark.sql.functions._  //经常用
    //import org.apache.spark.sql.expressions.scalalang.typed//用的少一点
  }
}

代码实现-2-StructuredStreaming实时版

  • 1.注意:
    • 如果要用SparkStreaming也行,但是SparkStreaming中没有DataFrame/DataSet-API处理起来麻烦
    • 如果是普通字符串,后续的分割处理都类似,那如果是该练习中的json那么就需要单独处理了,因为json需要解析
    • 所以该实时版中,重点在json数据的实时解析
  • 2.启动Kafka并开启kafka控制台生产者
    • 三台执行:
      • /export/servers/kafka/bin/kafka-server-start.sh /export/servers/kafka/config/server.properties>/dev/null" &
    • 一台执行:
      • /export/servers/kafka/bin/kafka-console-producer.sh --broker-list node01:9092 --topic spark_kafka
  • 3.手动往kafka发送实时日志消息
{
    
    "device":"Michael","deviceType": "people","signal": 15,"time": "2018-01-02 15:20:00"}
{
    
    "device":"jack","deviceType": "people","signal": 50,"time": "2018-01-02 15:20:00"}
{
    
    "device":"lenovo","deviceType": "computer","signal": 100,"time": "2018-01-02 15:20:00"}
{
    
    "device":"thunisoft","deviceType": "soft","signal": 30,"time": "2018-01-02 15:20:00"}
{
    
    "device":"book","deviceType": "soft","signal": 18,"time": "2018-01-02 15:20:00"}
{
    
    "device":"apache","deviceType": "soft","signal": 60,"time": "2018-01-02 15:20:00"}
{
    
    "device":"spark","deviceType": "soft","signal": 20,"time": "2018-01-02 15:20:00"}
{
    
    "device":"redmi","deviceType": "phone","signal": 10,"time": "2018-01-02 15:20:00"}
  • 4.导入pom
 <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.47</version>
    </dependency>
  • 5.代码实现
package cn.hanjiaxiaozhi.sql
​
import java.sql.Timestamp
​
import com.alibaba.fastjson.JSON
import org.apache.spark.SparkContext
import org.apache.spark.sql.{
    
    DataFrame, Dataset, SparkSession}/**
 * Author hanjiaxiaozhi
 * Date 2020/7/30 10:19
 * Desc 物联网设备数据分析-实时版-StructuredStreaming
 * {"device":"Michael","deviceType": "people","signal": 15,"time": "2018-01-02 15:20:00"}
 */
object IoTAnalysis2 {
    
    case class DeviceData(device: String, deviceType: String, signal: Double, time: Timestamp)def main(args: Array[String]): Unit = {
    
    
    //1.准备运行环境-StructuredStreaming-SparkSession
    val spark: SparkSession = SparkSession.builder().appName("IoTAnalysis").master("local[*]").getOrCreate()
    val sc: SparkContext = spark.sparkContext
    sc.setLogLevel("WARN")
    import spark.implicits._
​
    //2.加载数据-从Kafka实时加载json格式的物联网设备日志信息
    val kafkaDF: DataFrame = spark.readStream
      .format("kafka")
      .option("kafka.bootstrap.servers", "node01:9092")
      .option("subscribe", "spark_kafka")
      .load()//3.数据转换
    //从kafkaDF获取binary格式的Value并反序列化为String格式(json字符串)
    //val ds: Dataset[DeviceData] = kafkaDF.selectExpr("CAST(value AS DeviceData)").as[DeviceData] //错误:发送的json字符串,只能反序列化为json字符串,不能偷懒直接转为DeviceData
    val jsonStrDS: Dataset[String] = kafkaDF.selectExpr("CAST(value AS String)").as[String]
    //将jsonStrDS中的每一条json数据转为DeviceData,方便后续进行指标统计
    //jsonString==>样例类对象,可以使用阿里巴巴提供的工具类--fastjson
    val ds: Dataset[DeviceData] = jsonStrDS.map(jsonStr => {
    
    
      //注意:
      //Scala中获取类的字节码对象使用classOf
      //Java中获取类的字节码对象使用类名.class ,如果获取对象的: 对象.getClass, 如果获取全类路径对应的class对象: Class.forName("包类路径")
      //JSON.parseObject(jsonStr,DeviceData.class)//报错,因为这是java的写法,我们现在使用的Scala,得用Scala的写法
      JSON.parseObject(jsonStr, classOf[DeviceData])
    })//4.统计指标
    //需求:1.信号强度大于10的设备--过滤
    val result1: Dataset[DeviceData] = ds.filter(_.signal>10)
    //需求:2.各种设备类型的数量--分组-聚合
    val result2: Dataset[(String, Long)] = ds.groupByKey(_.deviceType).count()
    //需求:3.各种设备类型的平均信号强度 --分组再求平均
    import org.apache.spark.sql.expressions.scalalang.typed
    val result3: Dataset[(String, Double)] = ds.groupByKey(_.deviceType).agg(typed.avg(_.signal))//5.输出结果
    result1.writeStream
      .format("console")
      .outputMode("append")//result1中只有简单统计,没有聚合,不能使用complete,所以应该使用append(默认的追加模式)
      .option("truncate",false)
      .start()
      //.awaitTermination()
​
    result2.writeStream
      .format("console")
      .outputMode("complete")
      .option("truncate",false)
      .start()
      //.awaitTermination()
​
    result3.writeStream
      .format("console")
      .outputMode("complete")
      .option("truncate",false)
      .start()
      .awaitTermination()}
}

猜你喜欢

转载自blog.csdn.net/qq_46893497/article/details/114004216
今日推荐