Spark Streaming - 计算按键分组的键值对的统计数据

时间:2016-03-03 18:41:27

标签: scala apache-spark spark-streaming

背景: 我正在使用Spark Streaming来传输来自Kafka的事件,这些事件采用逗号分隔的键值对的形式 以下是将事件流式传输到我的spark应用程序的示例。

Key1=Value1, Key2=Value2, Key3=Value3, Key4=Value4,responseTime=200
Key1=Value5, Key2=Value6, Key3=Value7, Key4=Value8,responseTime=150
Key1=Value9, Key2=Value10, Key3=Value11, Key4=Value12,responseTime=100

输出

我想计算给定批次间隔中流中不同键分组的不同指标(平均值,计数等),例如

  1. Key1,Key2的平均响应时间(responseTime是每个事件中的一个键)
  2. 按Key1,Key2计算
  3. 到目前为止我的尝试:

    val stream = KafkaUtils
      .createDirectStream[String, String, StringDecoder, StringDecoder](
         ssc, kafkaParams, topicsSet)
    
    val pStream = stream.persist()
    
    val events: DStream[String] = pStream.flatMap(_._2.split(","))
    val pairs= events.map(data => data.split("=")).map(array => (array(0), array(1)))
    // pairs results in tuples of (Key1, Value1), (Key2, Value2) and so on.
    

    更新 - 03/04 密钥Key1,Key2 ......可以在传入流中无序到达。

    感谢您的输入/提示。

1 个答案:

答案 0 :(得分:2)

一种可能的解决方案是这样的:

  • 创建一个代表每条记录的案例类,以便我们不处理元组:

    case class Record(
      key1: String, key2: String, key3: String, key4: String, rt: Double)
    
  • 使用regexp解析记录并删除格式错误的条目:

    import scala.util.matching.Regex
    
    val recordPattern = new Regex(
      "^Key1=(.*?), ?Key2=(.*?), ?Key3=(.*?), ?Key4=(.*?), ?" ++
      "responseTime=(0-9+)$"
    )
    
    val records = pStream.map {
      case recordPattern(key1, key2, key3, key4, rt) =>
        Some(Record(key1, key2, key3, key4, rt.toDouble))
      case _ => None
    }.flatMap(x => x) // Drop malformed
    
  • 将数据重塑为键值对:

    val pairs = records.map(r => ((r.key1, r.key2), r.rt))
    
  • 创建分区程序并使用StatCounter汇总统计信息:

    import org.apache.spark.util.StatCounter
    import org.apache.spark.HashPartitioner
    
    val paritioner: HashPartitioner = ???
    
    pairs.combineByKey[StatCounter](
      StatCounter(_), _ merge _,  _ merge _, paritioner
    )
    
  • 提取感兴趣的字段:

    stats.mapValues(s => (s.count, s.mean))
    

你也可以尝试这样的无序数据,尽管我强烈建议上游修复:

val kvPattern = "(\\w+)=(\\w+)".r
val pairs = pStream.map(line => {
  val kvs = kvPattern.findAllMatchIn(line)
    .map(m => (m.group(1), m.group(2))).toMap

  // This will discard any malformed lines
  // (lack of key1, key2, lack or invalid format of responseTime)
  Try((
    (kvs("Key1"), kvs("Key2")), 
    kvs("responseTime").toDouble
  ))

}).flatMap(_.toOption)

并像以前一样继续。