我正在创建一个Spring Integration应用程序,该应用程序需要动态实例化IntegrationFlow
。我的流程如下所示:
kafkaListener -> intermediateChannel -> httpOutboundGateway
,其中httpOutboundGateway
通过ExpressionEvaluatingRequestHandlerAdvice
将错误路由到持久errorChannel
来建议。该errorChannel
有一个轮询器和一个IntegrationFlow
,它将返回错误返回到intermediateChannel
中。
当所有流和errorChannel
bean由Spring创建并自动自动装配时,该方案就像一个超级按钮。但是,当我尝试以编程方式实例化所有实例时,只有满意的路径有效(kafka -> intermediateChannel -> httpOutboundGateway
),但是errorChannel
没有收到数据-doSend
消息中的日志字符串未打印,并且流程没有重试错误。登录启动时未发生任何错误或警告,只是该建议无效。
我使用了here的一些建议,他们帮助注册了流程并让工作顺利了。但是,在注册不是流本身而是支持bean时,我似乎缺少了一些东西。
我还尝试在调试中检查相应的errorChannel
bean是否已连接到applicationContext
中,的确如此。可以通过errorChannelName
查找,并且可以在IntegrationFlow
注册时刻使用。
这是我的代码。
我在@PostConstruct
中手动创建一个错误通道,并注册它和流:
@PostConstruct
fun topology() {
rules.forEach { rule ->
val mainChannelName = rule.topicFrom + "-channel"
val errorChannelName = rule.topicFrom + "-error-channel"
val errorChannel = channelProducer.createPollableDatabaseChannel(errorChannelName)
val topicToChannelFlow = integrationFlowProducer.fromTopicToChannel(rule.topicFrom, mainChannelName)
val channelToEndpointFlow = integrationFlowProducer.
fromChannelToEndpoint(mainChannelName, rule.endpointDetails, errorChannelName)
val errorChannelToMainFlow = integrationFlowProducer.fromErrorChannelToMain(errorChannelName, mainChannelName)
integrationFlowContext.registration(topicToChannelFlow).register()
integrationFlowContext.registration(channelToEndpointFlow).addBean(errorChannel).register()
integrationFlowContext.registration(errorChannelToMainFlow).register()
}
}
这是channelProducer.createPollableDatabaseChannel(errorChannelName)
方法的实现:
fun createPollableDatabaseChannel(channelName: String): PollableChannel {
val queueChannel = object: QueueChannel(MessageGroupQueue(jdbcStore, channelName)) {
override fun doSend(message: Message<*>, timeout: Long): Boolean {
logger.info("sending message to error channel: $message")
return super.doSend(message, timeout)
}
override fun doReceive(timeout: Long): Message<*> {
val received = super.doReceive(timeout)
logger.info("received message: $received")
return received
}
}
return queueChannel
}
这是上面在integrationFlowProducer
中调用的方法:
fun fromTopicToChannel(topicFrom: String, receiverChannel: String): IntegrationFlow {
return IntegrationFlows
.from(
Kafka.messageDrivenChannelAdapter(
consumerFactory,
KafkaMessageDrivenChannelAdapter.ListenerMode.record, topicFrom
)
.configureListenerContainer { c ->
c.ackMode(ContainerProperties.AckMode.RECORD)
}
)
.log()
.channel(receiverChannel)
.get()
}
fun fromChannelToEndpoint(
channelFrom: String,
endpointDetails: EndpointDetails,
errorChannelName: String
): IntegrationFlow {
if (endpointDetails is RestEndpointDetails) {
return createRestIntegrationFlow(channelFrom, endpointDetails, errorChannelName)
}
throw UnsupportedOperationException("only rest endpoint supported")
}
fun fromErrorChannelToMain(errorChannelName: String, mainChannelName: String): IntegrationFlow {
return IntegrationFlows.from(errorChannelName)
.wireTap {f -> f.handle {t -> logger.info("Message read from error channel: " + t.payload.toString())}}
.transform<ErrorMessage, String> { extractPayloadFromErrorMessage(it) }
.channel(mainChannelName)
.get()
}
private fun createRestIntegrationFlow(
channelFrom: String,
endpointDetails: RestEndpointDetails,
errorChannelName: String
): IntegrationFlow {
return IntegrationFlows.from(channelFrom)
.wireTap { f -> f.handle { t -> logger.info("Message read from main channel: " + t.payload.toString()) } }
.handle<HttpRequestExecutingMessageHandler>(
Http.outboundGateway(
endpointDetails.url,
sslRestTemplate
)
.httpMethod(HttpMethod.POST)
.headerMapper(kafkaToHttpHeaderMapper)
.expectedResponseType(String::class.java)
) { c -> c.advice(failureAdvice(errorChannelName)) }
.nullChannel()
}
private fun extractPayloadFromErrorMessage(errorMessage: ErrorMessage) =
(errorMessage.payload as EvaluatingException).failedMessage!!.payload as String
private fun failureAdvice(errorChannelName: String): Advice {
val advice = ExpressionEvaluatingRequestHandlerAdvice()
advice.setFailureChannelName(errorChannelName)
return advice
}
按以下方式创建轮询器,这是一个自动初始化的bean:
@Bean(name = [PollerMetadata.DEFAULT_POLLER])
fun poller(deliveryTransactionInterceptor: TransactionInterceptor,
deliveryThreadPoolTaskExecutor: TaskExecutor): PollerMetadata {
return Pollers.fixedDelay(30, TimeUnit.SECONDS)
.advice(deliveryTransactionInterceptor)
.taskExecutor(deliveryThreadPoolTaskExecutor)
.get()
}
还有其他一些持久性设置,但是对于工作(自动初始化)的代码和当前代码,它们保持不变。如果需要它们,我也可以将其粘贴在这里。
Spring Integration的版本是5.1.4.RELEASE。
非常感谢您对此问题的帮助。
答案 0 :(得分:1)
由于您可以即时进行所有操作,因此看起来每个动态流都有一个ExpressionEvaluatingRequestHandlerAdvice
实例。我认为也必须将其注册为bean。流注册上的addBean()
是必经之路。我知道您需要重新编写一些代码,但实际上也必须像bean一样。