配置Spring Integration TCP向Socket Server发数据遇超时,求排查解决
问题场景
按以下步骤实现Spring Integration TCP向Socket Server发送数据:
- 启动Socket Server
- 运行Spring Integration TCP Outbound Gateway
- 预期Outbound Gateway可向Socket Server发送数据,且Socket Server能接收数据
但实际触发超时错误,未达到预期效果。
代码实现
public TcpOutboundGateway buildTcpGateway() { return buildGatewayInternal(getEndpoint()); } @Bean public TcpNetClientConnectionFactory initTcpNetClientConnectionFactory() { final TcpNetClientConnectionFactory connectionFactory = new TcpNetClientConnectionFactory(host, port); connectionFactory.setSingleUse(true); connectionFactory.afterPropertiesSet(); return connectionFactory; } @Bean public ThreadAffinityClientConnectionFactory initThreadAffinityClientConnectionFactory() { return new ThreadAffinityClientConnectionFactory(initTcpNetClientConnectionFactory()); } public TcpOutboundGateway buildGatewayInternal(String endpoint) { final TcpOutboundGateway gateway = new TcpOutboundGateway(); setHostAndPort(); gateway.setConnectionFactory(initThreadAffinityClientConnectionFactory()); gateway.setRequiresReply(false); gateway.setRequestTimeout(dmvConfig.getConnectTimeout()); gateway.setRemoteTimeout(dmvConfig.getTimeout()); final QueueChannel outputChannel = new QueueChannel(); final DirectChannel directChannel = new DirectChannel(); gateway.setOutputChannel(outputChannel); gateway.setReplyChannel(directChannel); gateway.start(); gateway.setUnsolicitedMessageChannelName("unsolicited"); final Message<String> message = new GenericMessage<String>("foo\r\n"); gateway.handleMessage(message); return gateway; }
错误堆栈
[ERROR] 2023-05-26 18:33:56.751 [main] TcpOutboundGateway - [error] - Tcp Gateway exception org.springframework.integration.MessageTimeoutException: Timed out waiting for response at org.springframework.integration.ip.tcp.TcpOutboundGateway.getReply(TcpOutboundGateway.java:293) ~[spring-integration-ip-5.5.15.jar:5.5.15] at org.springframework.integration.ip.tcp.TcpOutboundGateway.handleRequestMessage(TcpOutboundGateway.java:234) ~[spring-integration-ip-5.5.15.jar:5.5.15] at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:136) ~[spring-integration-core-5.5.15.jar:5.5.15] at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:56) ~[spring-integration-core-5.5.15.jar:5.5.15] at buildGatewayInternal(DmvGateway.java:114) ~[classes/:?] at buildTcpGateway(DmvGateway.java:84) ~[classes/:?] at DmvGatewayTest.testNetClient(DmvGatewayTest.java:70) ~[test-classes/:?] at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?] at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?] at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
排查分析与解决方案
核心问题点
TcpOutboundGateway 的请求-响应特性:
即使设置了setRequiresReply(false),TcpOutboundGateway本质是请求-响应模式组件,仍然会等待服务端回复直到超时。如果是单向发送(不需要服务端回复)的场景,应该使用**TcpSendingMessageHandler(TCP Outbound Channel Adapter)**而非Gateway。手动调用
handleMessage的时机问题:
在bean构建方法buildGatewayInternal中直接调用gateway.handleMessage(message),此时网关及通道的生命周期可能未完全初始化,且不符合Spring Integration的消息流转规范,应该通过消息通道发送消息,而非直接调用handler方法。连接工厂的重复创建与生命周期管理:
在buildGatewayInternal中每次调用initThreadAffinityClientConnectionFactory()都会创建新的连接工厂实例,导致该实例不受Spring容器管理,同时手动调用afterPropertiesSet()属于冗余操作(Spring会自动处理bean的初始化回调)。服务端回复缺失:
如果确实需要使用Gateway(需要响应),需确认Socket Server是否正确接收消息并返回响应,若服务端未回复,网关会一直等待直到触发超时。
具体修复步骤
- 切换到单向发送组件:若不需要服务端回复,替换TcpOutboundGateway为TcpSendingMessageHandler,示例:
@Bean public TcpSendingMessageHandler tcpOutboundAdapter() { TcpSendingMessageHandler adapter = new TcpSendingMessageHandler(); adapter.setConnectionFactory(initThreadAffinityClientConnectionFactory()); return adapter; } - 规范消息发送方式:通过Spring管理的消息通道发送消息,而非在bean构建阶段直接调用
handleMessage,例如定义input通道并注入发送:@Bean public DirectChannel tcpInputChannel() { return new DirectChannel(); } // 在需要发送消息的地方注入MessageChannel @Autowired private MessageChannel tcpInputChannel; // 发送消息 tcpInputChannel.send(new GenericMessage<>("foo\r\n")); - 复用Spring管理的连接工厂:在
buildGatewayInternal中注入已通过@Bean定义的ThreadAffinityClientConnectionFactory实例,而非每次创建新对象:public TcpOutboundGateway buildGatewayInternal(String endpoint, ThreadAffinityClientConnectionFactory connectionFactory) { final TcpOutboundGateway gateway = new TcpOutboundGateway(); // ...其他配置 gateway.setConnectionFactory(connectionFactory); // ...其他配置 return gateway; } - 检查服务端逻辑:若使用Gateway,确认Socket Server在接收消息后能正确返回响应,且响应的消息分隔符与客户端配置一致(默认基于换行符,需匹配你的
foo\r\n格式)。
内容的提问来源于stack exchange,提问作者Abhay Anand Mishra

