Netty heartbeat mechanism implementation (client and server)
Netty's heartbeat mechanism is an important means to maintain long connection effectiveness, which can detect whether the connection is alive and release invalid connections in time. The following introduces the complete implementation solution of the client and server.
1. Server-side implementation
1. Basic heartbeat detection
public class HeartbeatServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ();
// Add codec
(new StringDecoder());
(new StringEncoder());
// Heartbeat detection
// Parameter description: readerIdleTime, writerIdleTime, allIdleTime, time unit
(new IdleStateHandler(5, 0, 0, ));
(new HeartbeatServerHandler());
}
}
public class HeartbeatServerHandler extends ChannelInboundHandlerAdapter {
// Heartbeat loss counter
private int lossConnectCount = 0;
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (() == IdleState.READER_IDLE) {
lossConnectCount++;
if (lossConnectCount > 2) {
("Close inactive connection: " + ());
().close();
}
}
} else {
(ctx, evt);
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// Reset the counter if any message is received
if ("HEARTBEAT".equals(msg)) {
lossConnectCount = 0;
("Heartbeat received: " + ());
("HEARTBEAT_RESPONSE");
} else {
// Process other business messages
}
}
}
2. Complete heartbeat interaction solution
public class AdvancedHeartbeatServerHandler extends ChannelInboundHandlerAdapter {
private static final ByteBuf HEARTBEAT_SEQUENCE =
(("HEARTBEAT", CharsetUtil.UTF_8));
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleState state = ((IdleStateEvent) evt).state();
if (state == IdleState.READER_IDLE) {
// Read idle (no client message received)
("Read idle, close the connection: " + ());
();
} else if (state == IdleState.WRITER_IDLE) {
// Write free (you can actively send heartbeat packets)
("Write free, send heartbeat packet");
(HEARTBEAT_SEQUENCE.duplicate())
.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
}
} else {
(ctx, evt);
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
String message = (String) msg;
if ("HEARTBEAT_REQUEST".equals(message)) {
// Respond to client heartbeat
("HEARTBEAT_RESPONSE");
} else {
// Process business messages
}
}
}
2. Client implementation
1. Basic heartbeat realization
public class HeartbeatClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ();
(new StringDecoder());
(new StringEncoder());
// The client sets write idle detection (sends heartbeat regularly)
(new IdleStateHandler(0, 4, 0, ));
(new HeartbeatClientHandler());
}
}
public class HeartbeatClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (() == IdleState.WRITER_IDLE) {
// Send heartbeat when writing free
("HEARTBEAT");
("Client sends heartbeat");
}
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if ("HEARTBEAT_RESPONSE".equals(msg)) {
("Server heartbeat response received");
}
}
}
2. Complete heartbeat interaction solution
public class AdvancedHeartbeatClientHandler extends ChannelInboundHandlerAdapter {
private static final ByteBuf HEARTBEAT_SEQUENCE =
(("HEARTBEAT_REQUEST", CharsetUtil.UTF_8));
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// Send a heartbeat immediately after the connection is established
sendHeartbeat(ctx);
(ctx);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleState state = ((IdleStateEvent) evt).state();
if (state == IdleState.WRITER_IDLE) {
// Send heartbeat when writing free
sendHeartbeat(ctx);
} else if (state == IdleState.READER_IDLE) {
// Read idle (the server response was not received)
("The server does not respond, close the connection");
();
}
} else {
(ctx, evt);
}
}
private void sendHeartbeat(ChannelHandlerContext ctx) {
(HEARTBEAT_SEQUENCE.duplicate())
.addListener(future -> {
if (!()) {
("Heartbeat send failed: " + ());
}
});
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
String message = (String) msg;
if ("HEARTBEAT".equals(message)) {
// Respond to the server's heartbeat
("HEARTBEAT_RESPONSE");
} else if ("HEARTBEAT_RESPONSE".equals(message)) {
// Received the server's response to the client's heartbeat
("Ordinary heartbeat");
}
}
}
3. WebSocket heartbeat implementation
For WebSocket connections, the heartbeat mechanism requires special handling:
Server-side implementation
public class WebSocketHeartbeatServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
String text = ();
if ("HEARTBEAT".equals(text)) {
(new TextWebSocketFrame("HEARTBEAT_RESPONSE"));
} else {
// Handle other WebSocket messages
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent idleEvent = (IdleStateEvent) evt;
if (() == IdleState.READER_IDLE) {
();
} else if (() == IdleState.WRITER_IDLE) {
(new TextWebSocketFrame("HEARTBEAT"));
}
}
}
}
Client implementation
public class WebSocketHeartbeatClientHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
String text = ();
if ("HEARTBEAT".equals(text)) {
(new TextWebSocketFrame("HEARTBEAT_RESPONSE"));
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (() == IdleState.WRITER_IDLE) {
(new TextWebSocketFrame("HEARTBEAT"));
}
}
}
}
4. Best Practice Suggestions
-
Set the timeout reasonably:
- It is recommended to set the idle time to 60-120 seconds in the production environment.
- Set the free time of writing to 30-60 seconds
-
Heartbeat protocol design:
- Heartbeat messages using fixed formats (such as "HEARTBEAT")
- You can consider carrying a timestamp or serial number for debugging
-
Reconnect mechanism:
// Client reconnection example public class ReconnectHandler extends ChannelInboundHandlerAdapter { private final Bootstrap bootstrap; private int retries = 0; @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { if (retries < 3) { long delay = 1L << retries; retries++; ().eventLoop().schedule(() -> { ("Try to reconnect..." + retries); (); }, delay, ); } (); } }
-
Monitoring and logging:
- Record abnormal heartbeat
- Monitor connection survival rate
-
Performance considerations:
- Use shared ByteBuf as heartbeat message
- Avoid time-consuming operations in the heartbeat processor
Through the above implementation, a robust Netty heartbeat mechanism can be built to effectively maintain the reliability of long connections.
Considerations for writing idle detection in Netty Heartbeat Mechanism
In the Netty heartbeat mechanism, write idle (WRITER_IDLE) detection and read idle (READER_IDLE) detection have different application scenarios and considerations. Whether you need to use both depends on the specific business needs.
1. The main considerations for writing idle detection
-
The client actively keeps alive(Most common scenario)
- When the client needs to maintain a connection to the server (such as the mobile device connecting through the NAT gateway)
- Prevent intermediate devices (routers, firewalls, etc.) from being disconnected due to long-term no data flow
- Typical implementation: The client sends heartbeat packets regularly
-
Active detection on the server(Special scene)
- When the server needs to confirm whether the client is alive but the client cannot actively send a heartbeat
- In the bidirectional heartbeat detection mechanism
- Scenarios where the server needs to actively push data (such as real-time monitoring system)
-
Symmetrical heartbeat design
- In systems with high reliability requirements such as finance and payment
- Maintain active detection in both directions to improve connection reliability
2. Do I only need to read idle detection?
Scenarios where read idle detection can be used only:
-
Pure server detection mode
- The client will send data regularly (including business data and heartbeat)
- The server only needs to detect whether any data is received within the specified time
-
The client reliably sends heartbeats
- The client can ensure that the heartbeat packet is sent on time
- Stable network environment (such as intranet communication)
-
Resource saving considerations
- Reduce unnecessary write operations
- Simplify heartbeat logic
Scenarios where write idle detection is required at the same time:
-
Long connections in NAT environment
// Client configuration in typical NAT environment (new IdleStateHandler(0, 30, 0, )); // Only detect write idleness
-
Systems that require active maintenance on the server
// The server needs to keep the connection active (new IdleStateHandler(60, 30, 0, )); // Read and write detection
-
Two-way heartbeat verification
// Heartbeat design for high reliability system // Server: (new IdleStateHandler(60, 45, 0, )); // Client: (new IdleStateHandler(75, 30, 0, ));
3. Practical application suggestions
Recommended solution 1: Client unilateral heartbeat (most common)
// Client configuration
(new IdleStateHandler(0, 30, 0, )); // Only detect write idleness
(new HeartbeatClientHandler());
// Server configuration
(new IdleStateHandler(90, 0, 0, )); // Only detect read idle
Applicable scenarios: Most mobile applications, WebSocket communications, etc.
advantage:
- The client actively keeps active to avoid NAT timeout
- The server only needs to detect whether the client is alive
- Simple implementation
Recommended solution 2: Bidirectional heartbeat detection
// Server configuration
(new IdleStateHandler(60, 45, 0, ));
// Client configuration
(new IdleStateHandler(75, 30, 0, ));
Applicable scenarios:
- Financial payment system
- Communication of key IoT devices
- Scenarios with extremely high requirements for connection reliability
advantage:
- Confirm the status of the two-way connection
- Higher reliability
- One-way network interruption can be detected faster
Recommended solution 3: Adaptive heartbeat
// Can be dynamically adjusted according to network conditions
public class AdaptiveIdleStateHandler extends IdleStateHandler {
private boolean isMobileNetwork;
public AdaptiveIdleStateHandler() {
super(60, 30, 0, );
}
@Override
protected long nextDelay(IdleState state) {
if (isMobileNetwork && state == IdleState.WRITER_IDLE) {
return 25; // Send more frequently under mobile network
}
return (state);
}
}
4. Key decision-making factors
-
Network environment:
- Public network/NAT environment: write idle detection is required
- Intranet environment: You may just need to read idle detection
-
Client Type:
- Mobile devices: Actively keep alive (write free)
- Server: Usually, it only needs to detect whether the client is alive (read idle)
-
Business Requirements:
- Ordinary message push: unilateral detection is sufficient
- Financial transactions: Two-way testing is recommended
-
Resource consumption:
- Write idle detection will increase a small amount of network traffic
- Reading idle detection will not generate additional traffic
V. Typical cases
Case 1: IM instant messaging system
// Client (mobile device)
(new IdleStateHandler(0, 25, 0, )); // Write only idle
// Server side
(new IdleStateHandler(120, 0, 0, )); // Read-only idle
reason: Mobile devices need to maintain NAT mapping, and the server only needs to confirm whether the client is online
Case 2: IoT data collection
// Device side (client side)
(new IdleStateHandler(0, 60, 0, ));
// Server side
(new IdleStateHandler(180, 120, 0, ));
reason: The device may be in an unstable network environment and requires two-way detection
Summarize
Whether or not you need to write idle detection depends on the specific scenario:
- In most cases: The client needs to write idle detection (actively keep it alive), and the server only needs to read idle detection
- High reliability system: It is recommended to use two-way detection
- Intranet stable environment: Maybe just read idle detection
The best practice is to choose the appropriate combination method based on actual network conditions and business needs. For public network applications, especially mobile terminals, write idle detection is usually necessary.