Spring Integration DSL Tcp: как предотвратить чрезмерное ведение журнала соединения, отклоненного на стороне клиента - PullRequest
1 голос
/ 18 марта 2019

Я сделал так, чтобы и клиент, и сервер могли ожидать соединения друг с другом, поэтому они могут запускаться независимо в любом порядке. Решение , описанное здесь , делает именно то, что «из коробки», НО клиентская сторона продолжает печатать массивные трассировки стека в наших журналах, с java.net.ConnectException: Connection refused: connect, сопровождаемыми трассировкой стека длиной 46 строк, пока сервер не будет подключен и соединение случается. Это не идеально.

Мой вопрос: как применить мою собственную логику, чтобы точно настроить, что регистрировать и когда.

На данный момент я нахожу, что журналы печатаются org.springframework.integration.handler.LoggingHandler. Это действует как канал ошибок, и ошибки всегда отправляются туда. Я не могу найти, где находится этот набор, поэтому я могу, например, заменить его своей собственной реализацией. Мне удалось настроить свой собственный канал ошибок по умолчанию, но этот канал был добавлен вместе с предварительно настроенным каналом LoggingHandler, а не заменял его.

Другой подход может состоять в том, чтобы установить намного более длительное время ожидания при отправке первого сообщения. Я тоже с этим борюсь. Я пытался установить его на outboundGateway, как .handle(Tcp.outboundGateway(clientConnectionFactory).remoteTimeout(1_000_000L)), но это не имело никакого эффекта.

1 Ответ

0 голосов
/ 19 марта 2019

OK, решено.

Проблема была не в LoggingHandler или в каком-либо канале ошибок, а в том, что org.springframework.integration.ip.tcp.connection.TcpNetClientConnectionFactory.createSocket() выдает исключение, если сервер не сразу готов, и TcpOutboundGateway затем регистрирует это исключениепо старинке;и только тогда ошибка отправляется на errorChannel, где она может быть отреагирована;и реакция SI по умолчанию состоит в том, чтобы напечатать это снова :) Это - то, что я первоначально не заметил, исключение зарегистрировано дважды.Второй журнал можно предотвратить с помощью пользовательского обработчика сообщений об ошибках, но не первого.

TcpNetClientConnectionFactory.createSocket() вызывает по умолчанию Java createSocket (), и нет возможности установить тайм-аут.Если получатель не готов, вызов метода почти мгновенно завершается неудачей.См. Запрос расширения JDK JDK-4414843 .

Возможное решение - переопределить TcpNetClientConnectionFactory.createSocket() для повторения попыток подключения к серверу, пока оно не будет успешным.

WaitingTcpNetClientConnectionFactory

public class WaitingTcpNetClientConnectionFactory extends TcpNetClientConnectionFactory {
    private final SocketConnectionListener socketConnectionListener;
    private final int waitBetweenAttemptsInMs;
    private final Logger log = LogManager.getLogger();

    public WaitingTcpNetClientConnectionFactory(
            String host, int port, 
            int waitBetweenAttemptsInMs, 
            SocketConnectionListener socketConnectionListener) {
        super(host, port);
        this.waitBetweenAttemptsInMs = waitBetweenAttemptsInMs;
        this.socketConnectionListener = socketConnectionListener;
    }

    @Override
    protected Socket createSocket(String host, int port) throws IOException {
        Socket socket = null;
        while (socket == null) {
            try {
                socket = super.createSocket(host, port);
                socketConnectionListener.onConnectionOpen();
            } catch (ConnectException ce) {
                socketConnectionListener.onConnectionFailure();
                log.warn("server " + host + ":" + port + " is not ready yet ..waiting");
                try {
                    Thread.sleep(waitBetweenAttemptsInMs);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new IOException("interrupted while wating between connection attempts", ie);
                }
            }
        }
        return socket;
    }
}

В качестве дополнительного бонуса я также устанавливаю успех или неудачу при предоставлении SocketConnectionListener, моего собственного пользовательского интерфейса, чтобы другие части приложения могли синхронизироваться с ним;например, дождитесь потоковой передачи, пока узел сервера / однорангового узла не будет готов.

Используйте WaitingTcpNetClientConnectionFactory так же, как TcpNetClientConnectionFactory.

HeartbeatClientConfig (только соответствующий бит):

@Bean
public TcpNetClientConnectionFactory clientConnectionFactory(
        ConnectionStatus connectionStatus) {
    TcpNetClientConnectionFactory connectionFactory = new WaitingTcpNetClientConnectionFactory("localhost", 7777, 2000, connectionStatus);
    connectionFactory.setSerializer(new ByteArrayLengthHeaderSerializer());
    connectionFactory.setDeserializer(new ByteArrayLengthHeaderSerializer());
    return connectionFactory;
}

Теперь он просто печатает:

INFO [           main] o.b.e.d.s.h.client.HeartbeatClientRun    : Started HeartbeatClientRun in 1.042 seconds (JVM running for 1.44)
WARN [ask-scheduler-1] h.c.WaitingTcpNetClientConnectionFactory : server localhost:7777 is not ready yet ..waiting
WARN [ask-scheduler-1] h.c.WaitingTcpNetClientConnectionFactory : server localhost:7777 is not ready yet ..waiting
WARN [ask-scheduler-1] h.c.WaitingTcpNetClientConnectionFactory : server localhost:7777 is not ready yet ..waiting
WARN [ask-scheduler-1] h.c.WaitingTcpNetClientConnectionFactory : server localhost:7777 is not ready yet ..waiting

Как обычно, полные исходники проекта доступны на моем git, вот соответствующий коммит .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...