2

I've a TCP client which was built using spring integration TCP and the server supports a keep alive message (ping/pong style). The connections were configured using a CachingClientConnectionFactory and I'd like to take advantage on this server feature. Here's my bean configuration:

private static final int SERIALIZER_HEADER_SIZE = 2;

/**
 * Serializer used by connection factory to send and receive messages
 */
@Bean
public ByteArrayLengthHeaderSerializer byteArrayLengthHeaderSerializer() {
    return new ByteArrayLengthHeaderSerializer(SERIALIZER_HEADER_SIZE);
}

@Bean
public AbstractClientConnectionFactory tcpClientConnectionFactory() {
    TcpNetClientConnectionFactory connFactory =
        new TcpNetClientConnectionFactory(props.getUrl(), props.getPort());
    connFactory.setSerializer(byteArrayLengthHeaderSerializer());
    connFactory.setDeserializer(byteArrayLengthHeaderSerializer());
    connFactory.setSoTimeout(props.getSoTimeout());
    if (props.isUseSSL()) {
        connFactory.setTcpSocketFactorySupport(new DefaultTcpNetSSLSocketFactorySupport(() -> {
            return SSLContext.getDefault();
        }));
    }

    return connFactory;
}

/**
 * Connection factory used to create TCP client socket connections
 */
@Bean
public AbstractClientConnectionFactory tcpCachedClientConnectionFactory() {
    CachingClientConnectionFactory cachingConnFactory =
        new CachingClientConnectionFactory(tcpClientConnectionFactory(), props.getMaxPoolSize());
    cachingConnFactory.setConnectionWaitTimeout(props.getMaxPoolWait());
    return cachingConnFactory;
}

Using the solution posted here Configure keep alive to keep connection alive all the time I can keep the connection opened but I also wanted to take leverage on those server keep alive messages and send those messages from time to time to check if the connection is still alive. This can improve the performance on the client side since it won't need to re-connect/create a new connection if the socket was closed.

Based on that, does anyone has a suggestion on how to implement this using spring integration?

3
  • 1
    It's not clear exactly what you mean. You can set soKeepAlive to true so the operating system will keep the sockets open by sending pings. Then if you don't set soTimeout the socket will remain open indefinitely. Commented Oct 24, 2017 at 19:41
  • The server will expect something like KEEP_ALIVE_REQUEST and will send back a KEEP_ALIVE_RESPONSE. My question was about using that to keep the connection open, but based on your response soKeepAlive and soTimeout together can do the trick. Commented Oct 24, 2017 at 21:37
  • Update: @GaryRussell The server will close the socket after 30 seconds of inactivity. Based on that, does spring integration has any feature which I could use to send those specific keep alive messages (in a background manner) so that the client socket could be re-used? Commented Nov 10, 2017 at 22:32

1 Answer 1

3

When using a simple client connection factory, it's easy enough to set up application-level heartbeat messages with an @InboundChannelAdapter.

Simple example:

@SpringBootApplication
public class So46918267Application {

    public static void main(String[] args) throws IOException {
        // Simulated Server
        final ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(1234);
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(() -> {
            try {
                Socket socket = server.accept();
                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                    if (line.equals("keep_alive")) {
                        socket.getOutputStream().write("OK\r\n".getBytes());
                    }
                }
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        });
        ConfigurableApplicationContext context = SpringApplication.run(So46918267Application.class, args);
        System.out.println("Hit enter to terminate");
        System.in.read();
        executor.shutdownNow();
        context.close();
        server.close();
    }

    @Bean
    public TcpNetClientConnectionFactory client() {
        return new TcpNetClientConnectionFactory("localhost", 1234);
    }

    @ServiceActivator(inputChannel = "toTcp")
    @Bean
    public TcpOutboundGateway gateway() {
        TcpOutboundGateway gateway = new TcpOutboundGateway();
        gateway.setConnectionFactory(client());
        return gateway;
    }

    // HEARTBEATS

    private final Message<?> heartbeatMessage = MessageBuilder.withPayload("keep_alive")
            .setReplyChannelName("heartbeatReplies")
            .build();

    @InboundChannelAdapter(channel = "toTcp", poller = @Poller(fixedDelay = "25000"))
    public Message<?> heartbeat() {
        return this.heartbeatMessage;
    }

    @ServiceActivator(inputChannel = "heartbeatReplies")
    public void reply(byte[] reply) {
        System.out.println(new String(reply));
    }

}

When using the CachingClientConnectionFactory, though, it's not clear why you would want to keep a pool of idle connections open. However, the way the pool works is the idle connections are kept in a queue so each request would go to the oldest connection and the connection is returned to the end of the queue.

Adding maxMessagesPerPoll would emit that number of messages on each poll and...

@InboundChannelAdapter(channel = "toTcp", 
    poller = @Poller(fixedDelay = "25000", maxMessagesPerPoll = "5"))

would keep up to 5 connections open. It won't open new connections (if there's at least one) but if the pool contains 5 or more connections, at least 5 will be kept open. If there are no open connections, it will open just one.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much for your quick response, that was exactly what I was looking for. Every time the client has to open a new connection it takes some time so we wanted to maintain a few connections opened in the pool.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.