问题
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