我正在使用Alpakka AMQP库(https://developer.lightbend.com/docs/alpakka/current/amqp.html)在响应流中处理RabbitMQ消息并将其转储到Kafka中。
我们使用的是Avro和Schema Registry,因此格式错误的消息无法通过验证,需要根据具体情况进行处理。我们想要的行为是该应用程序死了,并在30秒-1分钟后重新启动,并且在此期间,我们能够使用UI将消息从队列中拉出,以查看问题所在。
由于某种原因,我的nack()
似乎并没有释放该消息,并且它停留在Unacked
状态,直到释放后才可以查看。一个人怎么做?我将提供一段代码:
Flow[CommittableIncomingMessage]
.map { msg =>
rabbitReceivedCounter.increment()
reconciliationGauge.increment()
val json = msg.message.bytes.utf8String
val record = Try(converter.convert(json)) match {
case Success(result) => result
case Failure(e) => e match {
case e: JsonToAvroConversionException ⇒
val errorMsg = s"Failed to serialize to avro: ${e.getMessage} - Field: ${e.getFieldName} - Value: ${e.getValue}"
failurePublisher.publishError(errorMsg, StatusCodes.BadRequest.intValue)
kafkaFailureCounter.increment()
Await.result(msg.nack(), Duration.Undefined)
throw e
nack()
是一个java.util.Future
,因此,为了进行测试,我在其周围扔了一个Await
,以确保问题不在于我在信号发生之前就抛出了错误放在兔子上。
答案 0 :(得分:1)
在流中引发异常时,导致错误的元素通常会丢失。一种想法是将Avro转换卸载到既返回转换结果又返回原始消息的actor:这种方法将允许您根据消息是否可转换为ack
或nack
消息。阿夫罗。
例如,演员可能看起来像这样:
case class AvroResult(avro: Option[Avro], msg: CommittableIncomingMessage)
// ^ change this to whatever type you're using
class ConverterActor extends Actor {
val converter = ???
def receive = {
case msg: CommittableIncomingMessage =>
try {
val json = msg.message.bytes.utf8String
val avro = converter.convert(json)
sender() ! AvroResult(Some(avro), msg)
} catch {
case _ =>
sender() ! AvroResult(None, msg)
}
}
}
然后,您可以在自己的信息流中讯问这位演员:
val converterActor = system.actorOf(Props[ConverterActor])
val source: Source[Avro, _] = amqpSource
.ask[AvroResult](converterActor)
.mapAsync(1) {
case AvroResult(Some(avro), msg) => // ack
msg.ack().map(_ => Some(avro))
case AvroResult(None, msg) => // nack
msg.nack().map(_ => None)
}
.collect {
case Some(avro) => avro
}
上面的Source
向下游发出经ack
确认的经过Avro转换的消息。不可转换的邮件将被nack
拒绝,并且不会向下传递。