SpringBoot TCP网关报错:no output-channel or replyChannel header
SpringBoot应用需实现三方通信:外部客户端1通过TCP Socket发起请求,SpringBoot用TcpInboundGateway接收请求,再通过TcpOutboundGateway转发至外部客户端2,获取响应后返回给客户端1。运行时抛出**"no output-channel or replyChannel header available"**错误,已配置TcpInboundGateway的replyChannel,且将处理客户端2响应的服务输出通道指定为该通道,但未生效。
相关配置代码
//The inbound gateway @Bean public TcpInboundGateway tcpInboundGateway(AbstractServerConnectionFactory serverConnectionFactory) { TcpInboundGateway gateway = new TcpInboundGateway(); gateway.setConnectionFactory(serverConnectionFactory); gateway.setRequestChannel(requestChannel()); gateway.setLoggingEnabled(true); gateway.setReplyChannel(backToPos()); gateway.setReplyTimeout(60000); return gateway; } // The outbound gateway @Bean @ServiceActivator(inputChannel = "tcpUpslOutgateChannel") public MessageHandler tcpOutboundGateway() { TcpOutboundGateway gateway = new TcpOutboundGateway(); gateway.setConnectionFactory(upslClientConnectionFactory()); gateway.setUnsolicitedMessageChannel(unsolicitedChannel()); gateway.setOutputChannelName("forUpslReplyChannel"); return gateway; } //Service that handles the response @ServiceActivator(inputChannel = "forUpslReplyChannel", outputChannel = "backToPos") public String processResponseMessageFromUpslForPurchase(Message<String> messageFull) { return processResponseMessageForPurchase(messageFull); }
错误日志
2024-07-12T08:47:07.307+01:00 INFO 23544 --- [terminal-management] [pool-2-thread-2] c.r.t.s.t.ResponseService : still in the response service 2024-07-12T08:47:07.311+01:00 DEBUG 23544 --- [terminal-management] [pool-2-thread-2] o.s.i.ip.tcp.TcpInboundGateway : failure occurred in gateway sendAndReceive: error occurred in message handler [org.springframework.integration.handler.BridgeHandler@25207619] 2024-07-12T08:47:07.311+01:00 ERROR 23544 --- [terminal-management] [pool-2-thread-2] o.s.i.i.tcp.connection.TcpNetConnection : Exception sending message: GenericMessage [payload=byte[809], headers={ip_tcp_remotePort=55560, ip_connectionId=127.0.0.1:55560:30002:6a5442c5-0c0d-4217-aab2-fe49776b6d6c, ip_localInetAddress=/127.0.0.1, ip_address=127.0.0.1, id=c841c491-79dc-5e17-4e95-6f7ac6052b79, ip_hostname=127.0.0.1, timestamp=1720770425566}] org.springframework.messaging.MessageHandlingException: error occurred in message handler [org.springframework.integration.handler.BridgeHandler@25207619] at org.springframework.integration.support.utils.IntegrationUtils.wrapInHandlingExceptionIfNecessary(IntegrationUtils.java:191) ~[spring-integration-core-6.3.0.jar:6.3.0] at org.springframework.integration.handler.AbstractMessageHandler.doHandleMessage(AbstractMessageHandler.java:108) at org.springframework.integration.ip.tcp.connection.TcpNetConnection.run(TcpNetConnection.java:206) ~[spring-integration-ip-6.3.0.jar:6.3.0] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na] at java.base/java.lang.Thread.run(Thread.java:840) ~[na:na] Caused by: org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:504) ~[spring-integration-core-6.3.0.jar:6.3.0] at org.springframework.integration.handler.AbstractMessageProducingHandler.doProduceOutput(AbstractMessageProducingHandler.java:357) ~[spring-integration-core-6.3.0.jar:6.3.0] at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:286) ~[spring-integration-core-6.3.0.jar:6.3.0]
- 响应链路丢失请求上下文:
TcpInboundGateway接收请求时会自动在消息头中注入replyChannel(对应配置的backToPos通道),但TcpOutboundGateway硬编码了outputChannelName,导致外部客户端2的响应直接进入forUpslReplyChannel,此时响应消息未携带原始请求的replyChannel头,后续转发到backToPos通道时,无法找到对应的返回目标。 - 通道实例不匹配:如果
backToPos()方法每次调用都创建新的DirectChannel实例,那么TcpInboundGateway配置的replyChannel与响应处理服务指定的outputChannel不是同一个通道对象,会导致消息无法正确路由。 - BridgeHandler路由失败:日志中的
BridgeHandler异常,本质是消息没有有效的输出通道配置,要么是消息头缺失replyChannel,要么是通道绑定关系错误。
方案1:移除TcpOutboundGateway的硬编码输出通道
删除TcpOutboundGateway的setOutputChannelName配置,让它依赖消息头的replyChannel自动返回响应,这样响应会直接回到原始请求的返回通道,无需额外配置响应处理服务的输出通道。
修改后的TcpOutboundGateway代码:
// The outbound gateway @Bean @ServiceActivator(inputChannel = "tcpUpslOutgateChannel") public MessageHandler tcpOutboundGateway() { TcpOutboundGateway gateway = new TcpOutboundGateway(); gateway.setConnectionFactory(upslClientConnectionFactory()); gateway.setUnsolicitedMessageChannel(unsolicitedChannel()); // 移除setOutputChannelName配置 return gateway; }
同时可以简化响应处理服务的配置,不需要指定outputChannel:
//Service that handles the response @ServiceActivator(inputChannel = "forUpslReplyChannel") public String processResponseMessageFromUpslForPurchase(Message<String> messageFull) { return processResponseMessageForPurchase(messageFull); }
方案2:确保通道实例全局唯一
检查backToPos通道的Bean定义,确保每次调用返回的是同一个实例,避免因实例不匹配导致的路由失败:
@Bean public MessageChannel backToPos() { return new DirectChannel(); }
确保TcpInboundGateway的setReplyChannel和响应处理服务的outputChannel都引用这个Bean实例,而不是每次创建新对象。
方案3:手动传递replyChannel头(可选)
如果必须保留outputChannelName配置,需要在响应处理服务中手动将原始请求的replyChannel头传递到响应消息中:
//Service that handles the response @ServiceActivator(inputChannel = "forUpslReplyChannel") public Message<String> processResponseMessageFromUpslForPurchase(Message<String> messageFull) { String processedPayload = processResponseMessageForPurchase(messageFull); // 复制原始消息头,保留replyChannel信息 return MessageBuilder.withPayload(processedPayload) .copyHeaders(messageFull.getHeaders()) .build(); }
注意:此方案需要确保从请求入口到响应处理的整个链路中,消息头没有被过滤或修改。
内容的提问来源于stack exchange,提问作者serethewind

