利用广播变量来进行数据的传输

6 篇文章 0 订阅
2 篇文章 0 订阅
package report

import config.ConfigHelper
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.SparkSession
import scalikejdbc.{DB, SQL}
import scalikejdbc.config.DBs
import utils.MakeATPKpi

//利用广播变量来进行数据的传输
object TrainTimeBroadcastAnalysis {
  def main(args: Array[String]): Unit = {
    //session
    val session=SparkSession
      .builder()
      //设置进程的名称
      .appName(this.getClass.getName)
      //设置是否是在本地运行
      //*代表几个线程
      .master("local[*]")
      //序列化
      .config("spark.serializer",ConfigHelper.serializer)
      .getOrCreate()
    //导入隐式转换
    import session.implicits._
    //读取原始日志
    val frame = session.read.parquet(args(0))
    //读取列车出场时间字典表
    val trainTimeSource = session.sparkContext.textFile(args(1))
    //读取以后对数据以"|"为标准进行切分
    //spilt中的regax属性里面的"\\"是转译符号,除非后面的符号是","或者";",不然类似于"|"这样的前面都要加上"\\"
    //limit
    //什么都不加:300T//      剪切完:300T
    //加-1:300T//      剪切完:300T “” “” “” “” “” “”
    val rddSpilt: RDD[Array[String]] = trainTimeSource.map(_.split("\\|",-1))
    //清洗数据
    val rddFilted: RDD[Array[String]] = rddSpilt.filter(_.length>=2)
    //搞成元组rdd
    val rddTuple: RDD[(String, String)] = rddFilted.map(arr=>(arr(0),arr(1)))
    //先收集再转换成map
//    val rddArr: Array[(String, String)] = rddTuple.collect()
//    val rddMap: Map[String, String] = rddArr.toMap
    //这个比上面的两行更高效
    val rddMap: collection.Map[String, String] = rddTuple.collectAsMap()
    //创建广播变量
    val trainTimeBro: Broadcast[collection.Map[String, String]] = session.sparkContext.broadcast(rddMap)
    //处理数据
    val rddResult: RDD[(String, List[Int])] = frame.map(row => {
      //获取车号
      val trainId = row.getAs[String]("MPacketHead_TrainID")
      //获取列车的出厂map
      val trainMap: collection.Map[String, String] = trainTimeBro.value
      //获取列车出厂的时间
      //第一个参数是凭借trainId去找trainTime
      //第二个参数是找不到trainTime就返回一个默认值
      val trainTime = trainMap.getOrElse(trainId, ",,,,")
      //获取kpi
      //使用MakeATPKpi样例类中的makeKpi方法,参数为row
      //这条语句在map中,map可以理解为有返回值的循环,因此参数为row,表示每一行都要获取数据
      val list: List[Int] = MakeATPKpi.makeKpi(row)
      //以列车的出厂时间为key,list为value,返回到map
      (trainTime, list)
    })
      //转换成rdd,因为只有rdd中才有reduceByKey方法
      .rdd
      //进行数据聚合

      //zip函数是把两个list聚合
      //开发中,需要聚合的文段
      //举个例子:
      //RDD中有如下元素
      //kv
      //((a,b),List(7,8,9))
      //((a,b),List(1,2,3))
      //((a,b),List(4,5,6))
      //((c,d),List(4,5,6))
      //。。。。。。
      //reducebykey后要对v进行操作
      //原理将v中的list前后zip再map每个元素进行逐个元组元素(a,b)的累加。
      //a代表v的前一个元素,b代表v的后一个元素
      //如val a=List(7,8,9)
      //val b=List  (1,2,3)
      //val k=  a zip  b  =((7,1),(8,2)(9,3))
      //k map (tp=>tp._1+tp._2)  ----- > (8,10,12)
      //如此循环 结果:
      //新RDD中的元素:
      //((a,b),List(12,15,18))
      //((c,d),List(4,5,6))
      //。。。。。。
      .reduceByKey {
      (list1, list2) => list1 zip list2 map (tp => tp._1 + tp._2)
    }
    //写入mysql中,用scalikejdbc写
    DBs.setup()
    //在driver端创建一个mysql链接,发送到从节点端
    //一个链接被n个从节点公用,排队,十分影响效率
    //在excutor端中每一条数据创建一个链接,每次都获取开线程,关线程,浪费时间
    //每个excutor用一个线程
    rddResult.foreachPartition(partition=>{
      DB.localTx{implicit session=>
        //遍历
        partition.foreach(tp=>{
          SQL("insert into TrainTimeBroadcastAnalysis values (?,?,?,?,?,?,?,?,?,?,?)")
            //把rddResult中的几个值都加入到insert语句中
            .bind(tp._1,tp._2(0),tp._2(1),tp._2(2),tp._2(3),tp._2(4),tp._2(5),tp._2(6),tp._2(7),tp._2(8),tp._2(9))
            .update()
            .apply()
        })
      }
    })
    //释放资源
    session.stop()
  }

}

样例类
MakeATPKpi.scala

package utils

import org.apache.commons.lang.StringUtils
import org.apache.spark.sql.Row

object MakeATPKpi {
  def makeKpi(row:Row)={
    //获取atperror
    val atpError = row.getAs[String]("MATPBaseInfo_AtpError")
    //判断指标
    //如果atpError不为空,前面为1,如果atpError为空,那么前面为0
    //先判断atpError不为空,如果为空,则执行下面的else语句,全部输出为0
    val listAtpError: List[Int] = if (StringUtils.isNotEmpty(atpError)) {
      val listError: List[Int] =
        if (atpError.equals("车载主机")) {
          List[Int](1, 0, 0, 0, 0, 0, 0, 0)
        } else if (atpError.equals("无线传输单元")) {
          List[Int](0, 1, 0, 0, 0, 0, 0, 0)
        } else if (atpError.equals("应答器信息接收单元")) {
          List[Int](0, 0, 1, 0, 0, 0, 0, 0)
        } else if (atpError.equals("轨道电路信息读取器")) {
          List[Int](0, 0, 0, 1, 0, 0, 0, 0)
        } else if (atpError.equals("测速测距单元")) {
          List[Int](0, 0, 0, 0, 1, 0, 0, 0)
        } else if (atpError.equals("人机交互接口单元")) {
          List[Int](0, 0, 0, 0, 0, 1, 0, 0)
        } else if (atpError.equals("列车接口单元")) {
          List[Int](0, 0, 0, 0, 0, 0, 1, 0)
        } else if (atpError.equals("司法记录单元")) {
          List[Int](0, 0, 0, 0, 0, 0, 0, 1)
        } else {
          //这是为了防止获取的atpError的值都不符合上述判断条件的情况
          List[Int](0, 0, 0, 0, 0, 0, 0, 0)
        }
      //两个list的拼接要用++
      List[Int](1) ++ listError
    } else {
      //如果atpError为空的情况,说明这行的这个属性没有值,那么全取为0即可
      List[Int](0, 0, 0, 0, 0, 0, 0, 0, 0)
    }
    //创建一个容器用来存放标签
    //两个list的拼接要用++
    //这个是为了在整合以后统计总共多少条数据用,相当于数量
    val list: List[Int] = List[Int](1) ++ listAtpError
    list
  }

}

相关配置文件
application.conf

#配置文件
#配置压缩格式
parquet.code="snappy"
#配置序列化方式
spark.serializer="org.apache.spark.serializer.KryoSerializer"
#配置jdbc链接
jdbc.url="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"
jdbc.driver="com.mysql.jdbc.Driver"
jdbc.user="root"
jdbc.password="000000"
#配置scalikejdbc链接
db.default.url="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"
db.default.driver="com.mysql.jdbc.Driver"
db.default.user="root"
db.default.password="000000"

ConfigHelper.scala

package config

import com.typesafe.config.{Config, ConfigFactory}

object ConfigHelper {
  //加载配置文件
  private lazy val load: Config = ConfigFactory.load()
  //加载压缩格式
  val parquetCode: String = load.getString("parquet.code")
  //加载序列化方式
  val serializer: String = load.getString("spark.serializer")
  //加载jdbc
  val url: String = load.getString("jdbc.url")
  val driver: String = load.getString("jdbc.driver")
  val user: String = load.getString("jdbc.user")
  val password: String = load.getString("jdbc.password")
  //加载scalikejdbc
  val url2: String = load.getString("db.default.url")
  val driver2: String = load.getString("db.default.driver")
  val user2: String = load.getString("db.default.user")
  val password2: String = load.getString("db.default.password")

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值