基於 Netty 的 Socket 通訊中,Server 按順序 1、2、3、4 發送數據包,Client 接收卻變為 1、3、2、4... 出現亂序。
ChannelPipeline 中是否有非線程安全的 @Sharable Handler。ChannelInboundHandler.channelRead 雖由 EventLoop 串行調用,但若業務邏輯提交到線程池異步處理,完成順序不確定。LengthFieldBasedFrameDecoder、DelimiterBasedFrameDecoder 或自定義協議解碼),導致半包、粘包被誤認為獨立消息。ByteBuf 內容與長度,確認消息邊界完整。ChannelOutboundBuffer 亂序 Bug。System.nanoTime() + 序列號,導出 CSV 分析。-Dio.netty.leakDetection.level=ADVANCED,觀察 ChannelOutboundBuffer 入隊出隊順序。EmbeddedChannel 單獨測試編解碼器、Handler 邏輯。| 層面 | 措施 |
|---|---|
| 協議層 | 每個數據包攜帶遞增序列號 (sequenceId) |
| 客戶端 | 接收端維護 PriorityQueue 或 TreeMap 按序列號緩存,按序交付上層 |
| 服務端 | 單線程 EventLoop 串行寫入,避免多線程搶佔 channel.write() |
| 編解碼 | 務必使用標準 FrameDecoder 保證消息邊界完整 |
public class OrderedMessageHandler extends SimpleChannelInboundHandler<Message> {
private final Map<Long, Message> buffer = new TreeMap<>();
private long expectedSeq = 1;
@Override
protected void channelRead0(ChannelHandlerContext ctx, Message msg) {
buffer.put(msg.getSequenceId(), msg);
flushReadyMessages(ctx);
}
private void flushReadyMessages(ChannelHandlerContext ctx) {
while (buffer.containsKey(expectedSeq)) {
Message msg = buffer.remove(expectedSeq);
ctx.fireChannelRead(msg); // 按序傳遞給下一個 Handler
expectedSeq++;
}
}
}
TCP 協議保證字節流有序,但應用層消息邊界、並發處理、編解碼錯誤均可能導致感知上的亂序。系統化排查:時間戳日誌 → 抓包確認網絡層 → 單元測試隔離編解碼 → 檢查並發模型。根治方案:協議加序列號 + 接收端有序緩存重排。