Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.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
Scala 连接2个kafka流时出现问题(使用自定义时间戳提取器)_Scala_Apache Kafka_Apache Kafka Streams - Fatal编程技术网

Scala 连接2个kafka流时出现问题(使用自定义时间戳提取器)

Scala 连接2个kafka流时出现问题(使用自定义时间戳提取器),scala,apache-kafka,apache-kafka-streams,Scala,Apache Kafka,Apache Kafka Streams,我在加入2个卡夫卡流时遇到问题,无法从我的活动字段中提取日期。当我没有定义自定义TimeStampExtractor时,联接工作正常,但当我定义自定义TimeStampExtractor时,联接不再工作。我的拓扑结构非常简单: val builder = new StreamsBuilder() val couponConsumedWith = Consumed.`with`(Serdes.String(), getAvroCouponSerde(schemaRegistryHost, s

我在加入2个卡夫卡流时遇到问题,无法从我的活动字段中提取日期。当我没有定义自定义TimeStampExtractor时,联接工作正常,但当我定义自定义TimeStampExtractor时,联接不再工作。我的拓扑结构非常简单:

val builder = new StreamsBuilder()

val couponConsumedWith = Consumed.`with`(Serdes.String(),
  getAvroCouponSerde(schemaRegistryHost, schemaRegistryPort))
val couponStream: KStream[String, Coupon] = builder.stream(couponInputTopic, couponConsumedWith)

val purchaseConsumedWith = Consumed.`with`(Serdes.String(),
  getAvroPurchaseSerde(schemaRegistryHost, schemaRegistryPort))
val purchaseStream: KStream[String, Purchase] = builder.stream(purchaseInputTopic, purchaseConsumedWith)

val couponStreamKeyedByProductId: KStream[String, Coupon] = couponStream.selectKey(couponProductIdValueMapper)
val purchaseStreamKeyedByProductId: KStream[String, Purchase] = purchaseStream.selectKey(purchaseProductIdValueMapper)

val couponPurchaseValueJoiner = new ValueJoiner[Coupon, Purchase, Purchase]() {

  @Override
  def apply(coupon: Coupon, purchase: Purchase): Purchase = {
      val discount = (purchase.getAmount * coupon.getDiscount) / 100
      new Purchase(purchase.getTimestamp, purchase.getProductid, purchase.getProductdescription, purchase.getAmount - discount)
  }
}

val fiveMinuteWindow = JoinWindows.of(TimeUnit.MINUTES.toMillis(10))
val outputStream: KStream[String, Purchase] = couponStreamKeyedByProductId.join(purchaseStreamKeyedByProductId,
  couponPurchaseValueJoiner,
  fiveMinuteWindow
  )

outputStream.to(outputTopic)

builder.build()
正如我所说的,当我不使用自定义时间戳提取器时,这段代码就像一个符咒,但当我通过将StreamsConfig.DEFAULT\u TIMESTAMP\u提取器\u CLASS\u CONFIG设置为我的自定义提取器类(我已经仔细检查了该类是否正确提取了日期)来使用时,连接不再工作

我通过运行单元测试并向其传递以下事件来测试拓扑:

    val coupon1 = new Coupon("Dec 05 2018 09:10:00.000 UTC", "1234", 10F)
    // Purchase within the five minutes after the coupon - The discount should be applied
    val purchase1 = new Purchase("Dec 05 2018 09:12:00.000 UTC", "1234", "Green Glass", 25.00F)
    val purchase1WithDiscount = new Purchase("Dec 05 2018 09:12:00.000 UTC", "1234", "Green Glass", 22.50F)
    val couponRecordFactory1 = couponRecordFactory.create(couponInputTopic, "c1", coupon1)
    val purchaseRecordFactory1 = purchaseRecordFactory.create(purchaseInputTopic, "p1", purchase1)

    testDriver.pipeInput(couponRecordFactory1)
    testDriver.pipeInput(purchaseRecordFactory1)
    val outputRecord1 = testDriver.readOutput(outputTopic,
      new StringDeserializer(),
      JoinTopologyBuilder.getAvroPurchaseSerde(
        schemaRegistryHost,
        schemaRegistryPort).deserializer())
    OutputVerifier.compareKeyValue(outputRecord1, "1234", purchase1WithDiscount)
不确定选择新密钥的步骤是否删除了正确的日期。我已经测试了很多组合,但运气不佳:(


非常感谢您的帮助!

我不确定这一点,因为我不知道您对代码进行了多少测试,但我的猜测是:

1) 您的代码使用默认的时间戳提取器,因为它使用您将记录作为时间戳记录发送到管道中的时间,所以基本上它可以工作,因为在您的测试中,您可以不停顿地一个接一个地发送数据

2) 您正在使用
TopologyTestDriver
进行测试! 请注意,它对于将业务代码和拓扑作为一个单元进行测试非常有用(我有哪些作为输入,哪些是正确的相应输出),但这些测试中没有运行Kafka Stream应用程序

在您的情况下,您可以使用
TopologyTestDriver
类中的
advanceWallClockTime(long)
方法来模拟系统时间行走

如果要启动拓扑,必须对嵌入式kafka群集进行集成测试(kafka库中有一个运行正常!)


如果有帮助,请告诉我:-)

谢谢您的回复。我昨天在做这个,我想我发现了问题。正如您所说,我正在使用TopologyTestDriver运行测试,当您初始化TopologyTestDriver类时,它使用initialWallClockTime,如果您不提供值,TopologyTestDriver将获取currentTimeMillis:

public TopologyTestDriver(Topology topology, Properties config) {
    this(topology, config, System.currentTimeMillis());
} 
还有一个构造函数允许您传入initialWallClockTime。我一直在测试这个方法,但出于某种原因,它不适合我

总之,我的解决方案是使用当前时间戳创建购买和优惠券对象。我仍然在使用我的自定义时间戳提取器,但不是硬编码日期,我总是得到当前的时间戳,这样连接就可以正常工作


我对我的最终解决方案不太满意,因为我不知道为什么initialWallClockTime对我不起作用,但至少现在测试工作正常

您的自定义提取器是否返回当前时间戳?另外,查看
KStreamKStreamJoinProcessor#process()
以了解处理器的功能。感谢您回复@MatthiasJ.Sax。我的自定义提取器从事件的timestamp字段中提取时间戳。我发现了问题(请看下面的答案)再次感谢!最初的挂钟时间不应对IMHO产生任何影响。连接是在事件时间定义的,如果您从负载中提取时间戳,它应该可以工作。对于使用默认时间戳提取器:这似乎是可行的,因为您的输入记录也将从使用挂钟时间初始化的工厂获取时间戳。总的来说,这似乎没有什么意义。我也不知道你所说的“我仍在使用我的自定义时间戳提取器,但我总是获取当前时间戳,而不是硬编码日期,这样连接工作正常。”这是我最初的想法@MatthiasJ.Sax,但似乎挂钟时间对流连接的方式有影响(至少使用TopologyTestDriver)。我注意到,当我试图找到问题时,我在时间戳提取器中返回currentTimeMillis,测试开始工作。无论如何,我已将测试推送到github repo,以便您可以更好地了解我试图解释的内容: