Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/apache-spark/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Apache spark Spark结构化流媒体是否可以进行适当的事件时间会话?_Apache Spark_Apache Spark Sql_Spark Structured Streaming - Fatal编程技术网

Apache spark Spark结构化流媒体是否可以进行适当的事件时间会话?

Apache spark Spark结构化流媒体是否可以进行适当的事件时间会话?,apache-spark,apache-spark-sql,spark-structured-streaming,Apache Spark,Apache Spark Sql,Spark Structured Streaming,一直在玩Spark结构化流媒体和mapGroupsWithState(具体遵循Spark源代码中的示例)。鉴于我的用例,我想确认mapGroupsWithState中存在的一些限制 就我而言,会话是用户的一组不间断活动,这样就不会有两个按时间顺序排列(按事件时间,而不是处理时间)的事件被超过一些开发人员定义的持续时间分隔(通常为30分钟) 在开始编写代码之前,一个示例会有所帮助: {"event_time": "2018-01-01T00:00:00", "user_id": "mike"} {

一直在玩Spark结构化流媒体和
mapGroupsWithState
(具体遵循Spark源代码中的示例)。鉴于我的用例,我想确认
mapGroupsWithState
中存在的一些限制

就我而言,会话是用户的一组不间断活动,这样就不会有两个按时间顺序排列(按事件时间,而不是处理时间)的事件被超过一些开发人员定义的持续时间分隔(通常为30分钟)

在开始编写代码之前,一个示例会有所帮助:

{"event_time": "2018-01-01T00:00:00", "user_id": "mike"}
{"event_time": "2018-01-01T00:01:00", "user_id": "mike"}
{"event_time": "2018-01-01T00:05:00", "user_id": "mike"}
{"event_time": "2018-01-01T00:45:00", "user_id": "mike"}
对于上面的流,会话定义为30分钟的非活动期。在流媒体环境中,我们应该结束一个会话(第二个会话尚未完成):

[
{
“用户id”:“mike”,
“startTimestamp”:“2018-01-01T00:00:00”,
“结束时间戳”:“2018-01-01T00:05:00”
}
]

现在考虑以下火花驱动程序:

导入java.sql.Timestamp
导入org.apache.spark.sql.{Row,SparkSession}
导入org.apache.spark.sql.execution.streaming.MemoryStream
导入org.apache.spark.sql.types.StructType
导入org.apache.spark.sql.functions_
导入org.apache.spark.sql.streaming.{GroupState,GroupStateTimeout}
对象结构SessionV2{
def main(参数:数组[字符串]):单位={
val spark=火花会话
建设者
.master(“本地[2]”)
.appName(“StructRedSessionationRedux”)
.getOrCreate()
spark.sparkContext.setLogLevel(“警告”)
导入spark.implicits_
隐式val ctx=spark.sqlContext
val输入=内存流[字符串]
val EVENT_SCHEMA=new StructType()
.add($“事件时间”.string)
.add($“用户id”.string)
val events=input.toDS()
.select(从_json($“value”,事件_模式)。别名(“json”))
.选择($“json.*”)
.withColumn(“事件时间”,至时间戳($“事件时间”))
.withWatermark(“事件时间”、“1小时”)
events.printSchema()
val sessionized=事件
.groupByKey(row=>row.getAs[String](“用户id”))
.mapGroupsWithState[SessionState,SessionOutput](GroupStateTimeout.EventTimeTimeTimeout){
大小写(userId:String,events:Iterator[Row],state:GroupState[SessionState])=>
println(s“用户${userId}的状态更新(当前水印:${new Timestamp(state.getCurrentWatermarkMs())})”
如果(说明,请说明){
println(s“用户${userId}已超时,正在发送最终输出。”)
val finalOutput=会话输出(
userId=userId,
starttimestamps=state.get.starttimestamps,
endTimestampMs=state.get.endTimestampMs,
durationMs=state.get.durationMs,
过期=真
)
//删除此用户的状态
state.remove()
最终输出
}否则{
val timestamps=events.map(u.getAs[Timestamp](“event_time”).getTime.toSeq
println(s“用户${userId}有新事件(min:${new Timestamp(timestamps.min)},max:${new Timestamp(timestamps.max)})。”
val newState=if(state.exists){
println(s“用户${userId}具有现有状态。”)
val oldState=state.get
会话状态(
starttimestamps=math.min(oldState.starttimestamps,timestamps.min),
endtimestamps=math.max(oldState.endtimestamps,timestamps.max)
)
}否则{
println(s“用户${userId}没有现有状态。”)
会话状态(
startTimestampMs=timestamps.min,
endtimestamps=timestamps.max
)
}
state.update(newState)
state.setTimeoutTimestamp(newState.endTimestamps,“30分钟”)
println(s“用户${userId}状态已更新。超时现在设置为${new Timestamp(newState.endtimestamps+(30*60*1000))}”)
会话输出(
userId=userId,
starttimestamps=state.get.starttimestamps,
endTimestampMs=state.get.endTimestampMs,
durationMs=state.get.durationMs,
过期=错误
)
}
}
val eventsQuery=会话化
.writeStream
.queryName(“事件”)
.outputMode(“更新”)
.格式(“控制台”)
.start()
input.addData(
“{”事件时间:“2018-01-01T00:00:00”,“用户id:“mike”}”,
“{”事件时间:“2018-01-01T00:01:00”,“用户id:“mike”}”,
“{”事件时间:“2018-01-01T00:05:00”,“用户id:“mike”}”
)
input.addData(
“{”事件时间:“2018-01-01T00:45:00”,“用户id:“mike”}”
)
eventsQuery.processAllAvailable()
}
案例类SessionState(startTimestampMs:Long,endTimestampMs:Long){
def durationMs:Long=endtimestamps-starttimestamps
}
case类SessionOutput(userId:String,starttimestamps:Long,endtimestamps:Long,durationMs:Long,expired:Boolean)
}
该程序的输出为:

root
 |-- event_time: timestamp (nullable = true)
 |-- user_id: string (nullable = true)

state update for user mike (current watermark: 1969-12-31 19:00:00.0)
User mike has new events (min: 2018-01-01 00:00:00.0, max: 2018-01-01 00:05:00.0).
User mike has no existing state.
User mike state updated. Timeout now set to 2018-01-01 00:35:00.0
-------------------------------------------
Batch: 0
-------------------------------------------
+------+----------------+--------------+----------+-------+
|userId|startTimestampMs|endTimestampMs|durationMs|expired|
+------+----------------+--------------+----------+-------+
|  mike|   1514782800000| 1514783100000|    300000|  false|
+------+----------------+--------------+----------+-------+

state update for user mike (current watermark: 2017-12-31 23:05:00.0)
User mike has new events (min: 2018-01-01 00:45:00.0, max: 2018-01-01 00:45:00.0).
User mike has existing state.
User mike state updated. Timeout now set to 2018-01-01 01:15:00.0
-------------------------------------------
Batch: 1
-------------------------------------------
+------+----------------+--------------+----------+-------+
|userId|startTimestampMs|endTimestampMs|durationMs|expired|
+------+----------------+--------------+----------+-------+
|  mike|   1514782800000| 1514785500000|   2700000|  false|
+------+----------------+--------------+----------+-------+
根据我的会话定义,第二批中的单个事件应触发会话状态到期,从而触发新会话。但是,由于水印(
2017-12-31 23:05:00.0
)未通过状态超时(
2018-01-01 00:35:00.0
),因此状态未过期,并且事件被错误地添加到现有会话中,尽管自上一批中的最新时间戳以来已超过30分钟

我认为会话状态过期的唯一方法是在批处理中接收到来自不同用户的足够多的事件,使水印提前超过
mike
的状态超时

我想也可能会弄乱流的水印,但我想不出如何才能完成我的用例


这准确吗?如何在Spark中正确执行基于事件时间的会话化,我是否遗漏了任何内容?

您提供的实现似乎并不正确