【发布时间】:2020-01-02 21:37:19
【问题描述】:
我目前有一个 Spring Integration 应用程序,它利用许多 TCP 入站和出站适配器组合来处理消息。所有这些适配器组合都使用相同的单个 MessageEndpoint 来处理请求,并使用相同的单个 MessagingGateway 来发送响应。
MessageEndpoint 的最终输出通道是DirectChannel,它也是 MessageGateway 的 DefaultRequestChannel。这个DirectChannel 使用默认的RoundRobinLoadBalancingStrategy,它正在循环搜索正确的出站适配器以发送给定的响应。当然,这种循环搜索并不总能在第一次搜索时找到合适的出站适配器,当它找不到时,它会相应地记录。这不仅会产生大量不需要的日志记录,而且还会引发一些性能问题,因为我预计在任何给定时间都会存在数百个入站/出站适配器组合。
我想知道是否有一种方法可以更紧密地关联入站和出站适配器,无需循环处理,并且每个响应都可以直接发送到相应的出站适配器?理想情况下,我希望以可以保持使用单个MessageEndpoint 和单个MessageGateway 的方式实现这一点。
注意:请将解决方案限制为使用入站/出站适配器组合的解决方案。我的实现无法使用 TcpInbound/TcpOutboundGateways,因为我需要向单个请求发送多个响应,据我所知,这只能通过使用入站/出站适配器来完成。
为了更清楚起见,下面是所描述的当前实现的精简版本。我试图清除任何不相关的代码只是为了让事情更容易阅读......
// Inbound/Outbound Adapter creation (part of a service that is used to dynamically create varying number of inbound/outbound adapter combinations)
public void configureAdapterCombination(int port) {
TcpNioServerConnectionFactory connectionFactory = new TcpNioServerConnectionFactory(port);
// Connection Factory registered with Application Context bean factory (removed for readability)...
TcpReceivingChannelAdapter inboundAdapter = new TcpReceivingChannelAdapter();
inboundAdapter.setConnectionFactory(connectionFactory);
inboundAdapter.setOutputChannel(context.getBean("sendFirstResponse", DirectChannel.class));
// Inbound Adapter registered with Application Context bean factory (removed for readability)...
TcpSendingMessageHandler outboundAdapter = new TcpSendingMessageHandler();
outboundAdapter.setConnectionFactory(connectionFactory);
// Outbound Adapter registered with Application Context bean factory (removed for readability)...
context.getBean("outboundResponse", DirectChannel.class).subscribe(outboundAdapter);
}
// Message Endpoint for processing requests
@MessageEndpoint
public class RequestProcessor {
@Autowired
private OutboundResponseGateway outboundResponseGateway;
// Direct Channel which is using Round Robin lookup
@Bean
public DirectChannel outboundResponse() {
return new DirectChannel();
}
// Removed additional, unrelated, endpoints for readability...
@ServiceActivator(inputChannel="sendFirstResponse", outputChannel="sendSecondResponse")
public Message<String> sendFirstResponse(Message<String> message) {
// Unrelated message processing/response generation excluded...
outboundResponseGateway.sendOutboundResponse("First Response", message.getHeaders().get(IpHeaders.CONNECTION_ID, String.class));
return message;
}
// Service Activator that puts second response on the request channel of the Message Gateway
@ServiceActivator(inputChannel = "sendSecondResponse", outputChannel="outboundResponse")
public Message<String> processQuery(Message<String> message) {
// Unrelated message processing/response generation excluded...
return MessageBuilder.withPayload("Second Response").copyHeaders(message.getHeaders()).build();
}
}
// Messaging Gateway for sending responses
@MessagingGateway(defaultRequestChannel="outboundResponse")
public interface OutboundResponseGateway {
public void sendOutboundResponse(@Payload String payload, @Header(IpHeaders.CONNECTION_ID) String connectionId);
}
解决方案:
@Artem 在下面的 cmets/answers 中的建议似乎可以解决问题。只是想快速记录一下我是如何在创建时向每个出站适配器添加replyChannel 的。
我所做的是创建两个由应用程序维护的地图。每当创建新的入站/出站适配器组合时都会填充第一个映射,它是ConnectionFactory 名称到replyChannel 名称的映射。第二个映射是ConnectionId 到replyChannel 名称的映射,它通过EventListener 填充到任何新的TcpConnectionOpenEvent。
请注意,每个TcpConnectionOpenEvent 都将根据建立连接的位置/方式定义ConnectionFactoryName 和ConnectionId 属性。
从那里,每当收到新请求时,我都会使用这些映射和Message 上的“ip_connectionId”标头将replyChannel 标头添加到消息中。第一个响应是通过从应用程序的上下文中手动获取相应的replyChannel(基于replyChannel 标头的值)并在该通道上发送响应来发送的。第二个响应是通过 Spring Integration 使用消息上的 replyChannel 标头发送的,正如 Artem 在他的响应中所描述的那样。
此解决方案是作为概念的快速证明实施的,并且仅适用于我当前的实施。包括这一点,希望能够快速启动其他查看者自己的实现/解决方案。
【问题讨论】:
-
请与我们分享您的配置,以便更好地了解发生了什么。
-
@Artem 你有什么特别想看的吗?我可以更新原始问题以包含您需要的内容。只是想确保我添加的内容确实是您所需要的。
-
请给我看至少一个从开始到结束的流程。我没有看到全貌。当然,很高兴看到这个项目,但我想从你这边是不可能的......
-
这个架构听起来很奇怪。我同意我们需要一些配置和一些关于您想要实现的确切目标的解释。
-
我已更新问题以包含配置。
标签: spring spring-boot tcp spring-integration