Location>code7788 >text

Netty heartbeat mechanism implementation (client and server)

Popularity:141 ℃/2025-04-25 16:52:21

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

  1. 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
  2. Heartbeat protocol design

    • Heartbeat messages using fixed formats (such as "HEARTBEAT")
    • You can consider carrying a timestamp or serial number for debugging
  3. 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, );
             }
             ();
         }
     }
  4. Monitoring and logging

    • Record abnormal heartbeat
    • Monitor connection survival rate
  5. 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

  1. 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
  2. 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)
  3. 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:

  1. 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
  2. The client reliably sends heartbeats

    • The client can ensure that the heartbeat packet is sent on time
    • Stable network environment (such as intranet communication)
  3. Resource saving considerations

    • Reduce unnecessary write operations
    • Simplify heartbeat logic

Scenarios where write idle detection is required at the same time:

  1. Long connections in NAT environment

    // Client configuration in typical NAT environment
     (new IdleStateHandler(0, 30, 0, )); // Only detect write idleness
  2. 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
  3. 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

  1. Network environment

    • Public network/NAT environment: write idle detection is required
    • Intranet environment: You may just need to read idle detection
  2. Client Type

    • Mobile devices: Actively keep alive (write free)
    • Server: Usually, it only needs to detect whether the client is alive (read idle)
  3. Business Requirements

    • Ordinary message push: unilateral detection is sufficient
    • Financial transactions: Two-way testing is recommended
  4. 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.