我正在尝试对从Kafka读取的(假)apache web服务器日志运行有状态的Spark Streaming计算。目标是"会话化"类似于this blog post
的网络流量唯一的区别是我想要"会话化" IP命中的每个页面,而不是整个会话。我能够在批处理模式下使用Spark从虚假网络流量文件中读取此内容,但现在我想在流式上下文中执行此操作。
日志文件从Kafka读取并解析为K/V
对(String, (String, Long, Long))
或
(IP, (requestPage, time, time))
。
然后我就groupByKey()
致电K/V pair
。在批处理模式下,这将产生:
(String, CollectionBuffer((String, Long, Long), ...)
或
(IP, CollectionBuffer((requestPage, time, time), ...)
在StreamingContext中,它产生一个:
(String, ArrayBuffer((String, Long, Long), ...)
喜欢这样:
(183.196.254.131,ArrayBuffer((/test.php,1418849762000,1418849762000)))
然而,随着下一个微分类(DStream)的到来,该信息被丢弃。
最终我想要的是ArrayBuffer
随着时间的推移填充,因为给定的IP继续交互并对其数据进行一些计算以进行"会话化"页面时间。
我认为实现这一目标的运营商是" updateStateByKey
。"我在运营商方面遇到了一些麻烦(我是Spark& Scala的新手);
感谢任何帮助。
到目前为止:
val grouped = ipTimeStamp.groupByKey().updateStateByKey(updateGroupByKey)
def updateGroupByKey(
a: Seq[(String, ArrayBuffer[(String, Long, Long)])],
b: Option[(String, ArrayBuffer[(String, Long, Long)])]
): Option[(String, ArrayBuffer[(String, Long, Long)])] = {
}
答案 0 :(得分:2)
我认为你正在寻找这样的东西:
def updateGroupByKey(
newValues: Seq[(String, ArrayBuffer[(String, Long, Long)])],
currentValue: Option[(String, ArrayBuffer[(String, Long, Long)])]
): Option[(String, ArrayBuffer[(String, Long, Long)])] = {
//Collect the values
val buffs: Seq[ArrayBuffer[(String, Long, Long)]] = (for (v <- newValues) yield v._2)
val buffs2 = if (currentValue.isEmpty) buffs else currentValue.get._2 :: buffs
//Convert state to buffer
if (buffs2.isEmpty) None else {
val key = if (currentValue.isEmpty) newValues(0)._1 else currentValue.get._1
Some((key, buffs2.foldLeft(new ArrayBuffer[(String, Long, Long)])((v, a) => v++a)))
}
}
答案 1 :(得分:2)
Gabor的回答让我开始走正确的道路,但这是一个产生预期输出的答案。
首先,对于我想要的输出:
(100.40.49.235,List((/,1418934075000,1418934075000), (/,1418934105000,1418934105000), (/contactus.html,1418934174000,1418934174000)))
我不需要groupByKey()
。 updateStateByKey
已经将值累积到Seq中,因此添加groupByKey
是不必要的(而且很昂贵)。 Spark用户强烈建议不要使用groupByKey
。
以下是有效的代码:
def updateValues( newValues: Seq[(String, Long, Long)],
currentValue: Option[Seq[ (String, Long, Long)]]
): Option[Seq[(String, Long, Long)]] = {
Some(currentValue.getOrElse(Seq.empty) ++ newValues)
}
val grouped = ipTimeStamp.updateStateByKey(updateValues)
这里updateStateByKey
传递一个函数(updateValues),它具有随时间累积的值(newValues)以及流中当前值的选项(currentValue)。然后它返回这些组合。getOrElse
是必需的,因为currentValue有时可能为空。感谢https://twitter.com/granturing获取正确的代码。