How to implement a keep alive connection on a TCP connection using spring integration?

﹥>﹥吖頭↗ 提交于 2021-02-10 18:44:44

问题


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?


回答1:


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.



来源:https://stackoverflow.com/questions/46918267/how-to-implement-a-keep-alive-connection-on-a-tcp-connection-using-spring-integr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!