类接口类型的数据集/数据流

kq0g1dla  于 2021-06-24  发布在  Flink
关注(0)|答案(1)|浏览(354)

我只是在flink中尝试使用scala类型的类。我定义了以下类型类接口:

trait LikeEvent[T] {
    def timestamp(payload: T): Int
}

现在,我想考虑 DataSetLikeEvent[_] 这样地:

// existing classes that need to be adapted/normalized (without touching them)
case class Log(ts: Int, severity: Int, message: String)
case class Metric(ts: Int, name: String, value: Double)

// create instances for the raw events
object EventInstance {

    implicit val logEvent = new LikeEvent[Log] {
        def timestamp(log: Log): Int = log.ts
    }

    implicit val metricEvent = new LikeEvent[Metric] {
        def timestamp(metric: Metric): Int = metric.ts
    }
}

// add ops to the raw event classes (regular class)
object EventSyntax {

    implicit class Event[T: LikeEvent](val payload: T) {
        val le = implicitly[LikeEvent[T]]
        def timestamp: Int = le.timestamp(payload)
    }
}

以下应用程序运行正常:

// set up the execution environment
val env = ExecutionEnvironment.getExecutionEnvironment

// underlying (raw) events
val events: DataSet[Event[_]] = env.fromElements(
  Metric(1586736000, "cpu_usage", 0.2),
  Log(1586736005, 1, "invalid login"),
  Log(1586736010, 1, "invalid login"),
  Log(1586736015, 1, "invalid login"),
  Log(1586736030, 2, "valid login"),
  Metric(1586736060, "cpu_usage", 0.8),
  Log(1586736120, 0, "end of world"),
)

// count events per hour
val eventsPerHour = events
  .map(new GetMinuteEventTuple())
  .groupBy(0).reduceGroup { g =>
    val gl = g.toList
    val (hour, count) = (gl.head._1, gl.size)
    (hour, count)
  }

eventsPerHour.print()

打印预期输出

(0,5)
(1,1)
(2,1)

但是,如果我这样修改syntax对象:

// couldn't make it work with Flink!
// add ops to the raw event classes (case class)
object EventSyntax2 {

  case class Event[T: LikeEvent](payload: T) {
    val le = implicitly[LikeEvent[T]]
    def timestamp: Int = le.timestamp(payload)
  }

  implicit def fromPayload[T: LikeEvent](payload: T): Event[T] = Event(payload)  
}

我得到以下错误:

type mismatch;
found   : org.apache.flink.api.scala.DataSet[Product with Serializable]
required: org.apache.flink.api.scala.DataSet[com.salvalcantara.fp.EventSyntax2.Event[_]]

因此,在这个信息的指引下,我做了以下改变:

val events: DataSet[Event[_]] = env.fromElements[Event[_]](...)

之后,错误变为:

could not find implicit value for evidence parameter of type org.apache.flink.api.common.typeinfo.TypeInformation[com.salvalcantara.fp.EventSyntax2.Event[_]]

我不明白为什么 EventSyntax2 导致这些错误 EventSyntax 编译和运行良好。为什么要在中使用case类 Package 器 EventSyntax2 比在中使用常规类更有问题 EventSyntax ?
不管怎样,我的问题有两个:
我怎样才能解决我的问题 EventSyntax2 ?
实现我的目标最简单的方法是什么?在这里,我只是为了学习而尝试使用类型类模式,但是一种更面向对象的方法(基于子类型)在我看来更简单。像这样:

// Define trait
trait Event {
    def timestamp: Int
    def payload: Product with Serializable // Any case class
}

// Metric adapter (similar for Log)
object MetricAdapter {

    implicit class MetricEvent(val payload: Metric) extends Event {
        def timestamp: Int = payload.ts
    }
}

然后简单地使用 val events: DataSet[Event] = env.fromElements(...) 总的来说。
注意,实现某个typeclass的类列表提出了一个类似的问题,但它考虑了一个简单的scala List 而不是燧石 DataSet (或 DataStream ). 我的问题的焦点是使用flink中的类型类模式来考虑异构流/数据集,以及它是否真的有意义,或者在这种情况下应该明确地支持一个常规特性并从中继承,如上所述。
顺便说一句,您可以在这里找到代码:https://github.com/salvalcantara/flink-events-and-polymorphism.

eqfvzcg8

eqfvzcg81#

简而言之:Flink无法推导 TypeInformation 在scala中,用于通配符类型
长话短说:你的两个问题都在问,什么是 TypeInformation ,如何使用,如何派生。 TypeInformation 是flink的内部类型系统,当数据在网络上乱序并存储在statebackend中时(当使用datastreamapi时),它使用该系统序列化数据。
序列化是数据处理中的一个主要性能问题,因此flink包含用于常见数据类型和模式的专用序列化程序。开箱即用,在它的java堆栈中,它支持所有jvm原语、pojo、flink元组、一些常见的集合类型和avro。类的类型是使用反射确定的,如果它与已知类型不匹配,它将返回到kryo。
在scalaapi中,类型信息是使用隐式派生的。scala数据集和datastreamapi上的所有方法都将其泛型参数作为类型类注解为隐式的。

def map[T: TypeInformation]

这个 TypeInformation 可以像任何类型类一样手动提供,也可以使用从flink导入的宏派生。

import org.apache.flink.api.scala._

这个宏装饰java类型堆栈,支持scala元组、scala case类和一些常见的scala std库类型。我之所以说decorator,是因为如果您的类不是这些类型中的一个,那么它可以并且将返回到java堆栈。
那么为什么版本1可以工作呢?
因为它是类型堆栈无法匹配的普通类,所以它将其解析为泛型类型并返回基于kryo的序列化程序。您可以从控制台测试它,并看到它返回一个泛型类型。

> scala> implicitly[TypeInformation[EventSyntax.Event[_]]]
res2: org.apache.flink.api.common.typeinfo.TypeInformation[com.salvalcantara.fp.EventSyntax.Event[_]] = GenericType<com.salvalcantara.fp.EventSyntax.Event>

版本2无法工作,因为它将类型识别为case类,然后以递归方式派生 TypeInformation 每个成员的示例。这对于通配符类型是不可能的,通配符类型与 Any 所以推导失败了。
一般来说,您不应该将flink用于异构类型,因为它将无法为您的工作负载派生有效的序列化程序。

相关问题