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, вот соответствующий коммит .