春季集成DSL TCP:如何防止在客户端拒绝过多的连接日志记录



我使客户端和服务器都可以互相连接,因此可以按任何顺序独立启动它们。此处描述的解决方案确实可以在开箱即用,但是客户端将在我们的日志中不断打印大量的堆栈跟踪,然后使用java.net.ConnectException: Connection refused: connect,然后是46行长堆栈跟踪,直到服务器启动并发生连接。这不是理想的。

我的问题是:如何应用我的自定义逻辑来微调记录什么和何时。

到目前为止,我发现日志是由org.springframework.integration.handler.LoggingHandler打印的。这起作用是在那里始终派出错误通道和错误。我找不到这组在哪里,所以我可以用自己的实现来替换它。我设法配置了自己的默认错误频道,但是与预先配置的LoggingHandler频道一起添加了该频道,而不是更换。

另一种方法可能是在发送第一消息时设置更长的超时。我也在努力。我试图将其设置为outboundGateway,为.handle(Tcp.outboundGateway(clientConnectionFactory).remoteTimeout(1_000_000L)),但没有任何效果。

好,已解决。

问题并不是在LoggingHandler或任何错误频道中真正存在,但是如果服务器未立即准备就绪,则org.springframework.integration.ip.tcp.connection.TcpNetClientConnectionFactory.createSocket()会引发异常,然后TcpOutboundGateway将此例外记录为老式的方式;只有这样,将错误派遣到errorChannel可以对其进行反应。默认的SI反应是再次打印它:)这是我最初没有注意到的,例外是两次记录的。可以通过使用自定义错误消息处理程序来预防第二个日志,而不是第一个日志。

TcpNetClientConnectionFactory.createSocket()调用默认Java的createsocket(),没有设置超时的选项。如果收件人尚未准备就绪,则方法调用几乎立即失败。请参阅JDK的增强请求JDK-4414843。

可能的解决方案是覆盖TcpNetClientConnectionFactory.createSocket()以重复连接尝试到服务器。

等待TcpnetClientConnectionFactory

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,因此应用程序的其他部分可以与之同步;例如,等待流媒体直到服务器/对等节点准备就绪。

使用WaitingTcpNetClientConnectionFactoryTcpNetClientConnectionFactory相同的方式。

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上可以提供完整的项目来源,这是相关的提交。

最新更新