簡體   English   中英

Spring 集成使用事件偵聽器處理連接關閉事件並在使用動態 TCP 路由時重新建立它

[英]Spring integration Handle Connection Close event with Event Listener and Re establish it while using Dynamic TCP Routing

我正在使用 spring 集成來創建請求/響應架構的流程,並從服務器接收任意數據。 在此階段之前,我檢查了 spring-integration github 的示例以及@Gary Russell 和@Artem Bilan 的建議。

這是我的網關界面

@Component
@MessagingGateway(defaultRequestChannel = "toTcp.input")
public interface ToTCP {
    byte[] send(String data, @Header("host") String host, @Header("port") int port, @Header("irregularMessageChannelName") String channelName);
    byte[] send(String data, @Header("host") String host, @Header("port") int port);
}

這是我的 TcpClientConfig

@Component
public class TcpClientConfig {
    @Bean
    public IntegrationFlow toTcp() {
        return f -> f.route(new TcpRouter());
    }
}

這是我的擴展 AbstractMessageRouter 的 TcpRouter

public class TcpRouter extends AbstractMessageRouter {


    private final Logger log = LoggerFactory.getLogger(TcpRouter.class);

    private final static int MAX_CACHED = 100; // When this is exceeded, we remove the LRU.

    private HashMap<String, Message<?>> connectionRegistery = new HashMap<>();

    private final LinkedHashMap<String, MessageChannel> subFlows =
        new LinkedHashMap<String, MessageChannel>(MAX_CACHED, .75f, true) {

            @Override
            protected boolean removeEldestEntry(Map.Entry<String, MessageChannel> eldest) {
                if (size() > MAX_CACHED) {
                    removeSubFlow(eldest);
                    return true;
                } else {
                    return false;
                }
            }

        };

    @Autowired
    private IntegrationFlowContext flowContext;

    @Override
    protected Collection<MessageChannel> determineTargetChannels(Message<?> message) {
        MessageChannel channel;
        boolean hasThisConnectionIrregularChannel = message.getHeaders().containsKey("irregularMessageChannelName");
        if (hasThisConnectionIrregularChannel) {
            channel = this.subFlows.get(message.getHeaders().get("host", String.class) + message.getHeaders().get("port") + ".extended");
        } else {
            channel = this.subFlows.get(message.getHeaders().get("host", String.class) + message.getHeaders().get("port"));
        }

        if (channel == null) {
            channel = createNewSubflow(message);
        }
        return Collections.singletonList(channel);
    }


    private MessageChannel createNewSubflow(Message<?> message) {
        String host = (String) message.getHeaders().get("host");
        Integer port = (Integer) message.getHeaders().get("port");

        boolean hasThisConnectionIrregularChannel = message.getHeaders().containsKey("irregularMessageChannelName");

        Assert.state(host != null && port != null, "host and/or port header missing");
        String flowRegisterKey;

        if (hasThisConnectionIrregularChannel) {
            flowRegisterKey = host + port + ".extended";
        } else {
            flowRegisterKey = host + port;
        }

        TcpNetClientConnectionFactory cf = new TcpNetClientConnectionFactory(host, port);
        cf.setSoTimeout(0);
        cf.setSoKeepAlive(true);

        ByteArrayCrLfSerializer byteArrayCrLfSerializer = new ByteArrayCrLfSerializer();
        byteArrayCrLfSerializer.setMaxMessageSize(1048576);

        cf.setSerializer(byteArrayCrLfSerializer);
        cf.setDeserializer(byteArrayCrLfSerializer);

        TcpOutboundGateway tcpOutboundGateway;
        if (hasThisConnectionIrregularChannel) {
            log.info("TcpRouter # createNewSubflow extended TcpOutboundGateway will be created");
            String irregularMessageChannelName = (String) message.getHeaders().get("irregularMessageChannelName");
            DirectChannel directChannel = getBeanFactory().getBean(irregularMessageChannelName, DirectChannel.class);
            tcpOutboundGateway = new ExtendedTcpOutboundGateway(directChannel);
        } else {
            log.info("TcpRouter # createNewSubflow extended TcpOutboundGateway will be created");
            tcpOutboundGateway = new TcpOutboundGateway();
        }

        tcpOutboundGateway.setConnectionFactory(cf);

        tcpOutboundGateway.setAdviceChain(Arrays.asList(new Advice[]{tcpRetryAdvice()}));

        IntegrationFlow flow = f -> f.handle(tcpOutboundGateway);

        IntegrationFlowContext.IntegrationFlowRegistration flowRegistration =
            this.flowContext.registration(flow)
                //.addBean(cf)
                .addBean("client_connection_" + flowRegisterKey, cf)
                .id(flowRegisterKey + ".flow")
                .register();

        MessageChannel inputChannel = flowRegistration.getInputChannel();

        this.subFlows.put(flowRegisterKey, inputChannel);
        this.connectionRegistery.put("client_connection_" + flowRegisterKey, message);

        return inputChannel;
    }

    private void removeSubFlow(Map.Entry<String, MessageChannel> eldest) {
        String hostPort = eldest.getKey();
        this.flowContext.remove(hostPort + ".flow");
    }

    @Bean
    public RequestHandlerRetryAdvice tcpRetryAdvice() {
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3);

        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(100);
        backOffPolicy.setMaxInterval(1000);
        backOffPolicy.setMultiplier(2);

        RetryTemplate retryTemplate = new RetryTemplate();
        retryTemplate.setRetryPolicy(retryPolicy);
        retryTemplate.setBackOffPolicy(backOffPolicy);

        RequestHandlerRetryAdvice tcpRetryAdvice = new RequestHandlerRetryAdvice();
        tcpRetryAdvice.setRetryTemplate(retryTemplate);

        // This allows fail-controlling
        tcpRetryAdvice.setRecoveryCallback(new ErrorMessageSendingRecoverer(failMessageChannel()));

        return tcpRetryAdvice;
    }

    @Bean
    public MessageChannel failMessageChannel() {
        return new DirectChannel();
    }

    @ServiceActivator(inputChannel = "failMessageChannel")
    public void messageAggregation(String in) {
        log.error("TcpRouter # connection retry failed with message : " + in);
    }

    @Autowired
    private ToTCP toTCP;

    @EventListener
    public void listen(TcpConnectionCloseEvent event) {
        String connectionFactoryName = event.getConnectionFactoryName();
        boolean isConnectionRegistered = this.connectionRegistery.containsKey(connectionFactoryName);
        if (isConnectionRegistered) {
            Message<?> message = this.connectionRegistery.get(connectionFactoryName);
            String host = (String) message.getHeaders().get("host");
            Integer port = (Integer) message.getHeaders().get("port");
            boolean hasThisConnectionIrregularChannel = message.getHeaders().containsKey("irregularMessageChannelName");
            if (hasThisConnectionIrregularChannel) {
                log.info("TcpRouter # listen # registered tcp connection with arbitrary message channel closed for host {} and port {}, it will open again !!", host, port);
                String unsolicitedMessageChannelName = (String) message.getHeaders().get("irregularMessageChannelName");
                toTCP.send(message.getPayload().toString(), host, port, unsolicitedMessageChannelName);
            } else {
                log.info("TcpRouter # listen # registered tcp connection closed for host {} and port {}, it will open again !!", host, port);
                toTCP.send(message.getPayload().toString(), host, port);
                            }
        } else {
            log.info("TcpRouter # listen # unregistered tcp connection closed, no action required.");
        }
    }
}

如果發生任何連接關閉事件,我可以使用事件偵聽器來處理它。 在事件監聽器中,我可以從addBean("client_connection_" + flowRegisterKey, cf)中注冊的connectionFactoryName中理解。 這是該部分的解決方案

在處理關閉哪個連接后,我應該再次打開它以繼續接收任意數據或在 TCP 服務器之間建立連接以發送任何請求......但我不確定我重新建立與發送數據的連接的方式。

我應該使用

@Autowired
private ToTCP toTCP;

在 TcpRouter class 再次發送消息

或者

我應該直接發送消息到

@Override
protected Collection<MessageChannel> determineTargetChannels(Message<?> message)

方法。 我對他們的工作行為感到困惑......你能給我一個正確的想法,幫助我使用更方便的方式讓 EventListener 重新建立連接嗎?

其實你是對的,重新連接請求與我調用它的初始時間相同。

在這種情況下我應該使用確定目標通道嗎?

不; 在事件偵聽器中執行與首先調用ToTCP的操作完全相同的操作(發送新請求並處理回復)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM