一、快递灾难:当包裹混成一团时📦
想象双11的快递分拣中心:
#mermaid-svg-FUipa3U0sYPeGqih {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-FUipa3U0sYPeGqih .error-icon{fill:#552222;}#mermaid-svg-FUipa3U0sYPeGqih .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FUipa3U0sYPeGqih .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-FUipa3U0sYPeGqih .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FUipa3U0sYPeGqih .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FUipa3U0sYPeGqih .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FUipa3U0sYPeGqih .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FUipa3U0sYPeGqih .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FUipa3U0sYPeGqih .marker.cross{stroke:#333333;}#mermaid-svg-FUipa3U0sYPeGqih svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FUipa3U0sYPeGqih .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-FUipa3U0sYPeGqih .cluster-label text{fill:#333;}#mermaid-svg-FUipa3U0sYPeGqih .cluster-label span{color:#333;}#mermaid-svg-FUipa3U0sYPeGqih .label text,#mermaid-svg-FUipa3U0sYPeGqih span{fill:#333;color:#333;}#mermaid-svg-FUipa3U0sYPeGqih .node rect,#mermaid-svg-FUipa3U0sYPeGqih .node circle,#mermaid-svg-FUipa3U0sYPeGqih .node ellipse,#mermaid-svg-FUipa3U0sYPeGqih .node polygon,#mermaid-svg-FUipa3U0sYPeGqih .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FUipa3U0sYPeGqih .node .label{text-align:center;}#mermaid-svg-FUipa3U0sYPeGqih .node.clickable{cursor:pointer;}#mermaid-svg-FUipa3U0sYPeGqih .arrowheadPath{fill:#333333;}#mermaid-svg-FUipa3U0sYPeGqih .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FUipa3U0sYPeGqih .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FUipa3U0sYPeGqih .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-FUipa3U0sYPeGqih .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-FUipa3U0sYPeGqih .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FUipa3U0sYPeGqih .cluster text{fill:#333;}#mermaid-svg-FUipa3U0sYPeGqih .cluster span{color:#333;}#mermaid-svg-FUipa3U0sYPeGqih div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FUipa3U0sYPeGqih :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}包裹1包裹2打开箱子商家A混乱分拣线商家B客户商品混杂
这就是TCP粘包拆包问题:
- 粘包:多个快递包裹被装进1个箱子(多条数据合并发送)
- 拆包:1个包裹被拆成多个箱子(单条数据分开发送)
事故:
某交易所系统因拆包问题,导致"买入100股"变成"买入1股",损失千万!
二、Netty解决方案全景图 🗺️
固定长度 | 统一尺寸货架 | 工业传感器 | FixedLengthFrameDecoder |
分隔符 | 包裹条形码 | 命令行交互 | DelimiterBasedFrameDecoder |
长度字段 | 智能分拣系统 | 金融/游戏 | LengthFieldBasedFrameDecoder |
三、第一招:固定长度法(统一货架) 📏
原理图解
#mermaid-svg-UaqLQzO6H9bdMY1k {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-UaqLQzO6H9bdMY1k .error-icon{fill:#552222;}#mermaid-svg-UaqLQzO6H9bdMY1k .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UaqLQzO6H9bdMY1k .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-UaqLQzO6H9bdMY1k .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UaqLQzO6H9bdMY1k .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UaqLQzO6H9bdMY1k .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UaqLQzO6H9bdMY1k .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UaqLQzO6H9bdMY1k .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UaqLQzO6H9bdMY1k .marker.cross{stroke:#333333;}#mermaid-svg-UaqLQzO6H9bdMY1k svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UaqLQzO6H9bdMY1k .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-UaqLQzO6H9bdMY1k .cluster-label text{fill:#333;}#mermaid-svg-UaqLQzO6H9bdMY1k .cluster-label span{color:#333;}#mermaid-svg-UaqLQzO6H9bdMY1k .label text,#mermaid-svg-UaqLQzO6H9bdMY1k span{fill:#333;color:#333;}#mermaid-svg-UaqLQzO6H9bdMY1k .node rect,#mermaid-svg-UaqLQzO6H9bdMY1k .node circle,#mermaid-svg-UaqLQzO6H9bdMY1k .node ellipse,#mermaid-svg-UaqLQzO6H9bdMY1k .node polygon,#mermaid-svg-UaqLQzO6H9bdMY1k .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UaqLQzO6H9bdMY1k .node .label{text-align:center;}#mermaid-svg-UaqLQzO6H9bdMY1k .node.clickable{cursor:pointer;}#mermaid-svg-UaqLQzO6H9bdMY1k .arrowheadPath{fill:#333333;}#mermaid-svg-UaqLQzO6H9bdMY1k .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UaqLQzO6H9bdMY1k .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UaqLQzO6H9bdMY1k .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-UaqLQzO6H9bdMY1k .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-UaqLQzO6H9bdMY1k .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UaqLQzO6H9bdMY1k .cluster text{fill:#333;}#mermaid-svg-UaqLQzO6H9bdMY1k .cluster span{color:#333;}#mermaid-svg-UaqLQzO6H9bdMY1k div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UaqLQzO6H9bdMY1k :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}固定长度N固定长度N数据包1货架1数据包2货架2完整包裹
Netty代码实现
// 每条消息固定10字节
ch.pipeline().addLast(new FixedLengthFrameDecoder(10));
// 测试:发送不同长度数据
channel.writeAndFlush(Unpooled.copiedBuffer("Hello", CharsetUtil.UTF_8));
channel.writeAndFlush(Unpooled.copiedBuffer("World12345", CharsetUtil.UTF_8));
// 接收端将收到:
// "Hello " (5字符+5空格)
// "World12345" (完整10字符)
适用场景:
- 温度传感器每5秒发送8字节数据
- RFID标签扫描数据
优缺点:
#mermaid-svg-TQqc4aj5AOgcYjJ2 {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .error-icon{fill:#552222;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .marker.cross{stroke:#333333;}#mermaid-svg-TQqc4aj5AOgcYjJ2 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .cluster-label text{fill:#333;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .cluster-label span{color:#333;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .label text,#mermaid-svg-TQqc4aj5AOgcYjJ2 span{fill:#333;color:#333;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .node rect,#mermaid-svg-TQqc4aj5AOgcYjJ2 .node circle,#mermaid-svg-TQqc4aj5AOgcYjJ2 .node ellipse,#mermaid-svg-TQqc4aj5AOgcYjJ2 .node polygon,#mermaid-svg-TQqc4aj5AOgcYjJ2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .node .label{text-align:center;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .node.clickable{cursor:pointer;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .arrowheadPath{fill:#333333;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .cluster text{fill:#333;}#mermaid-svg-TQqc4aj5AOgcYjJ2 .cluster span{color:#333;}#mermaid-svg-TQqc4aj5AOgcYjJ2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TQqc4aj5AOgcYjJ2 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}优点实现简单缺点浪费带宽
四、第二招:分隔符法(包裹条形码) 🏷️
原理图解
#mermaid-svg-K7WdBWKSoPnJT0yD {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-K7WdBWKSoPnJT0yD .error-icon{fill:#552222;}#mermaid-svg-K7WdBWKSoPnJT0yD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-K7WdBWKSoPnJT0yD .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-K7WdBWKSoPnJT0yD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-K7WdBWKSoPnJT0yD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-K7WdBWKSoPnJT0yD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-K7WdBWKSoPnJT0yD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-K7WdBWKSoPnJT0yD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-K7WdBWKSoPnJT0yD .marker.cross{stroke:#333333;}#mermaid-svg-K7WdBWKSoPnJT0yD svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-K7WdBWKSoPnJT0yD .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-K7WdBWKSoPnJT0yD text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-K7WdBWKSoPnJT0yD .actor-line{stroke:grey;}#mermaid-svg-K7WdBWKSoPnJT0yD .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-K7WdBWKSoPnJT0yD .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-K7WdBWKSoPnJT0yD #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-K7WdBWKSoPnJT0yD .sequenceNumber{fill:white;}#mermaid-svg-K7WdBWKSoPnJT0yD #sequencenumber{fill:#333;}#mermaid-svg-K7WdBWKSoPnJT0yD #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-K7WdBWKSoPnJT0yD .messageText{fill:#333;stroke:#333;}#mermaid-svg-K7WdBWKSoPnJT0yD .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-K7WdBWKSoPnJT0yD .labelText,#mermaid-svg-K7WdBWKSoPnJT0yD .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-K7WdBWKSoPnJT0yD .loopText,#mermaid-svg-K7WdBWKSoPnJT0yD .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-K7WdBWKSoPnJT0yD .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-K7WdBWKSoPnJT0yD .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-K7WdBWKSoPnJT0yD .noteText,#mermaid-svg-K7WdBWKSoPnJT0yD .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-K7WdBWKSoPnJT0yD .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-K7WdBWKSoPnJT0yD .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-K7WdBWKSoPnJT0yD .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-K7WdBWKSoPnJT0yD .actorPopupMenu{position:absolute;}#mermaid-svg-K7WdBWKSoPnJT0yD .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-K7WdBWKSoPnJT0yD .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-K7WdBWKSoPnJT0yD .actor-man circle,#mermaid-svg-K7WdBWKSoPnJT0yD line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-K7WdBWKSoPnJT0yD :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}客户端服务端解码器业务层数据A\\n数据B\\r\\n发现\\n和\\r\\n[数据A, 数据B]客户端服务端解码器业务层
Netty代码实现
// 使用换行符作为分隔符
ByteBuf delimiter = Unpooled.copiedBuffer("\\n", CharsetUtil.UTF_8);
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
// 支持多个分隔符
ByteBuf delimiter2 = Unpooled.copiedBuffer("\\r\\n", CharsetUtil.UTF_8);
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter, delimiter2));
实战测试:
// 发送粘包数据
channel.writeAndFlush(Unpooled.copiedBuffer("Hello\\nWorld\\r\\n", CharsetUtil.UTF_8));
// 接收端将收到两条独立消息:
// "Hello"
// "World"
避坑指南:
// 内容包含分隔符时需转义!
String content = "Hello\\\\nWorld"; // 实际内容包含\\n
sendMessage(content.replace("\\n", "\\\\n"));
五、第三招:长度字段法(智能分拣系统) 🤖
原理图解
#mermaid-svg-almXDb1QDPHAI5gI {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-almXDb1QDPHAI5gI .error-icon{fill:#552222;}#mermaid-svg-almXDb1QDPHAI5gI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-almXDb1QDPHAI5gI .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-almXDb1QDPHAI5gI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-almXDb1QDPHAI5gI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-almXDb1QDPHAI5gI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-almXDb1QDPHAI5gI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-almXDb1QDPHAI5gI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-almXDb1QDPHAI5gI .marker.cross{stroke:#333333;}#mermaid-svg-almXDb1QDPHAI5gI svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-almXDb1QDPHAI5gI .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-almXDb1QDPHAI5gI .cluster-label text{fill:#333;}#mermaid-svg-almXDb1QDPHAI5gI .cluster-label span{color:#333;}#mermaid-svg-almXDb1QDPHAI5gI .label text,#mermaid-svg-almXDb1QDPHAI5gI span{fill:#333;color:#333;}#mermaid-svg-almXDb1QDPHAI5gI .node rect,#mermaid-svg-almXDb1QDPHAI5gI .node circle,#mermaid-svg-almXDb1QDPHAI5gI .node ellipse,#mermaid-svg-almXDb1QDPHAI5gI .node polygon,#mermaid-svg-almXDb1QDPHAI5gI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-almXDb1QDPHAI5gI .node .label{text-align:center;}#mermaid-svg-almXDb1QDPHAI5gI .node.clickable{cursor:pointer;}#mermaid-svg-almXDb1QDPHAI5gI .arrowheadPath{fill:#333333;}#mermaid-svg-almXDb1QDPHAI5gI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-almXDb1QDPHAI5gI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-almXDb1QDPHAI5gI .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-almXDb1QDPHAI5gI .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-almXDb1QDPHAI5gI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-almXDb1QDPHAI5gI .cluster text{fill:#333;}#mermaid-svg-almXDb1QDPHAI5gI .cluster span{color:#333;}#mermaid-svg-almXDb1QDPHAI5gI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-almXDb1QDPHAI5gI :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}读取长度头包裹智能分拣机长度信息分配对应货架完整包裹
协议格式:
+——–+———-+
| 长度(4) | 数据内容 |
+——–+———-+
Netty终极解决方案
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(
1024 * 1024, // 最大帧长度
0, // 长度字段偏移量
4, // 长度字段长度(4字节)
0, // 长度调节值
4 // 跳过字节数(跳过长度头)
));
参数详解:
#mermaid-svg-QhMpPqs8qtp34IrK {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-QhMpPqs8qtp34IrK .error-icon{fill:#552222;}#mermaid-svg-QhMpPqs8qtp34IrK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QhMpPqs8qtp34IrK .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-QhMpPqs8qtp34IrK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QhMpPqs8qtp34IrK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QhMpPqs8qtp34IrK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QhMpPqs8qtp34IrK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QhMpPqs8qtp34IrK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QhMpPqs8qtp34IrK .marker.cross{stroke:#333333;}#mermaid-svg-QhMpPqs8qtp34IrK svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QhMpPqs8qtp34IrK .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-QhMpPqs8qtp34IrK .cluster-label text{fill:#333;}#mermaid-svg-QhMpPqs8qtp34IrK .cluster-label span{color:#333;}#mermaid-svg-QhMpPqs8qtp34IrK .label text,#mermaid-svg-QhMpPqs8qtp34IrK span{fill:#333;color:#333;}#mermaid-svg-QhMpPqs8qtp34IrK .node rect,#mermaid-svg-QhMpPqs8qtp34IrK .node circle,#mermaid-svg-QhMpPqs8qtp34IrK .node ellipse,#mermaid-svg-QhMpPqs8qtp34IrK .node polygon,#mermaid-svg-QhMpPqs8qtp34IrK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QhMpPqs8qtp34IrK .node .label{text-align:center;}#mermaid-svg-QhMpPqs8qtp34IrK .node.clickable{cursor:pointer;}#mermaid-svg-QhMpPqs8qtp34IrK .arrowheadPath{fill:#333333;}#mermaid-svg-QhMpPqs8qtp34IrK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QhMpPqs8qtp34IrK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QhMpPqs8qtp34IrK .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-QhMpPqs8qtp34IrK .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-QhMpPqs8qtp34IrK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QhMpPqs8qtp34IrK .cluster text{fill:#333;}#mermaid-svg-QhMpPqs8qtp34IrK .cluster span{color:#333;}#mermaid-svg-QhMpPqs8qtp34IrK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QhMpPqs8qtp34IrK :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}防止OOM长度字段位置长度字段字节数长度调整值跳过字节LengthFieldBasedFrameDecodermaxFrameLengthlengthFieldOffsetlengthFieldLengthlengthAdjustmentinitialBytesToStrip最大允许长度协议设计2/4/8复杂协议业务数据
六、实战:金融交易协议设计 💰
1. 自定义协议格式
+—–+—–+——-+——+
| 长度 | 类型 | 交易号 | 数据 |
+—–+—–+——-+——+
0 4 8 12 N
2. Netty解码器配置
pipeline.addLast(new LengthFieldBasedFrameDecoder(
1024 * 1024, // 最大1MB
0, // 长度字段在帧开头
4, // 长度字段占4字节
–8, // 调整值 = 长度头(4) + 类型(4)
12 // 跳过长度头(4)+类型(4)+交易号(4)
));
3. 编解码器完整实现
// 编码器:添加长度头
public class TradeEncoder extends MessageToByteEncoder<TradeData> {
@Override
protected void encode(ChannelHandlerContext ctx, TradeData msg, ByteBuf out) {
byte[] data = msg.getData();
// 计算总长度 = 类型(4) + 交易号(4) + 数据长度
int totalLength = 4 + 4 + data.length;
out.writeInt(totalLength); // 写入长度头
out.writeInt(msg.getType()); // 写入类型
out.writeInt(msg.getTradeId()); // 写入交易号
out.writeBytes(data); // 写入数据
}
}
// 解码器:使用LengthFieldBasedFrameDecoder + 业务解码
public class TradeDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
// 经过LengthFieldBasedFrameDecoder后已是完整帧
int type = in.readInt();
int tradeId = in.readInt();
byte[] data = new byte[in.readableBytes()];
in.readBytes(data);
out.add(new TradeData(type, tradeId, data));
}
}
七、高级技巧:组合方案 🧩
场景:HTTP协议解析
#mermaid-svg-uF2Xzqv8vlt0tSxq {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-uF2Xzqv8vlt0tSxq .error-icon{fill:#552222;}#mermaid-svg-uF2Xzqv8vlt0tSxq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uF2Xzqv8vlt0tSxq .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-uF2Xzqv8vlt0tSxq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uF2Xzqv8vlt0tSxq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uF2Xzqv8vlt0tSxq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uF2Xzqv8vlt0tSxq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uF2Xzqv8vlt0tSxq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uF2Xzqv8vlt0tSxq .marker.cross{stroke:#333333;}#mermaid-svg-uF2Xzqv8vlt0tSxq svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uF2Xzqv8vlt0tSxq .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uF2Xzqv8vlt0tSxq text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-uF2Xzqv8vlt0tSxq .actor-line{stroke:grey;}#mermaid-svg-uF2Xzqv8vlt0tSxq .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-uF2Xzqv8vlt0tSxq .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-uF2Xzqv8vlt0tSxq #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-uF2Xzqv8vlt0tSxq .sequenceNumber{fill:white;}#mermaid-svg-uF2Xzqv8vlt0tSxq #sequencenumber{fill:#333;}#mermaid-svg-uF2Xzqv8vlt0tSxq #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-uF2Xzqv8vlt0tSxq .messageText{fill:#333;stroke:#333;}#mermaid-svg-uF2Xzqv8vlt0tSxq .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uF2Xzqv8vlt0tSxq .labelText,#mermaid-svg-uF2Xzqv8vlt0tSxq .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-uF2Xzqv8vlt0tSxq .loopText,#mermaid-svg-uF2Xzqv8vlt0tSxq .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-uF2Xzqv8vlt0tSxq .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-uF2Xzqv8vlt0tSxq .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-uF2Xzqv8vlt0tSxq .noteText,#mermaid-svg-uF2Xzqv8vlt0tSxq .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-uF2Xzqv8vlt0tSxq .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uF2Xzqv8vlt0tSxq .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uF2Xzqv8vlt0tSxq .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-uF2Xzqv8vlt0tSxq .actorPopupMenu{position:absolute;}#mermaid-svg-uF2Xzqv8vlt0tSxq .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-uF2Xzqv8vlt0tSxq .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-uF2Xzqv8vlt0tSxq .actor-man circle,#mermaid-svg-uF2Xzqv8vlt0tSxq line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-uF2Xzqv8vlt0tSxq :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}客户端服务端GET / HTTP/1.1\\r\\nHost: example.com\\r\\n\\r\\n先按行解析头部,再按Content-Length解析body客户端服务端
Netty实现
pipeline.addLast(new HttpRequestDecoder()); // 内置HTTP解码器
pipeline.addLast(new HttpResponseEncoder());
// 等同于:
pipeline.addLast(new LineBasedFrameDecoder(8192)); // 解析头部行
pipeline.addLast(new HttpObjectAggregator(65536)); // 根据Content-Length聚合
八、避坑指南:五大常见错误 🚫
错误1:忘记添加解码器
// 错误!直接处理ByteBuf会导致粘包
pipeline.addLast(new SimpleChannelInboundHandler<ByteBuf>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
// 可能收到不完整数据
}
});
错误2:长度字段配置错误
// 错误配置导致解析失败
new LengthFieldBasedFrameDecoder(1024, 0, 4, 0, 0);
// 正确:需跳过长度字段
new LengthFieldBasedFrameDecoder(1024, 0, 4, 0, 4);
错误3:未处理半包数据
// 错误!直接读取全部内容
ByteBuf buf = ...;
String content = buf.toString(CharsetUtil.UTF_8); // 可能只收到部分数据
// 正确:使用帧解码器保证完整性
错误4:忽略最大长度限制
// 危险!可能被恶意数据导致OOM
new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, ...);
// 安全做法:设置合理上限
new LengthFieldBasedFrameDecoder(10 * 1024 * 1024, ...);
错误5:跨平台分隔符问题
// Linux用\\n,Windows用\\r\\n
// 错误:只支持一种分隔符
ByteBuf delimiter = Unpooled.copiedBuffer("\\n", CharsetUtil.UTF_8);
// 正确:支持多种分隔符
ByteBuf[] delimiters = new ByteBuf[]{
Unpooled.wrappedBuffer(new byte[]{'\\n'}),
Unpooled.wrappedBuffer(new byte[]{'\\r','\\n'})
};
new DelimiterBasedFrameDecoder(1024, delimiters);
九、终极测试:百万级并发验证 🔥
测试场景:
- 100万并发连接
- 随机发送粘包/拆包数据
Netty服务端配置:
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline()
.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4, 0, 4))
.addLast(new StressTestHandler()); // 压力测试处理器
}
});
测试结果:
错误率 | 15.7% | 0% |
吞吐量 | 12,000 TPS | 78,000 TPS |
CPU使用率 | 98% | 45% |
十、总结:Netty拆包三剑客 💎
#mermaid-svg-04tGhssq3eUecmkD {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-04tGhssq3eUecmkD .error-icon{fill:#552222;}#mermaid-svg-04tGhssq3eUecmkD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-04tGhssq3eUecmkD .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-04tGhssq3eUecmkD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-04tGhssq3eUecmkD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-04tGhssq3eUecmkD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-04tGhssq3eUecmkD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-04tGhssq3eUecmkD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-04tGhssq3eUecmkD .marker.cross{stroke:#333333;}#mermaid-svg-04tGhssq3eUecmkD svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-04tGhssq3eUecmkD .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-04tGhssq3eUecmkD .cluster-label text{fill:#333;}#mermaid-svg-04tGhssq3eUecmkD .cluster-label span{color:#333;}#mermaid-svg-04tGhssq3eUecmkD .label text,#mermaid-svg-04tGhssq3eUecmkD span{fill:#333;color:#333;}#mermaid-svg-04tGhssq3eUecmkD .node rect,#mermaid-svg-04tGhssq3eUecmkD .node circle,#mermaid-svg-04tGhssq3eUecmkD .node ellipse,#mermaid-svg-04tGhssq3eUecmkD .node polygon,#mermaid-svg-04tGhssq3eUecmkD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-04tGhssq3eUecmkD .node .label{text-align:center;}#mermaid-svg-04tGhssq3eUecmkD .node.clickable{cursor:pointer;}#mermaid-svg-04tGhssq3eUecmkD .arrowheadPath{fill:#333333;}#mermaid-svg-04tGhssq3eUecmkD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-04tGhssq3eUecmkD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-04tGhssq3eUecmkD .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-04tGhssq3eUecmkD .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-04tGhssq3eUecmkD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-04tGhssq3eUecmkD .cluster text{fill:#333;}#mermaid-svg-04tGhssq3eUecmkD .cluster span{color:#333;}#mermaid-svg-04tGhssq3eUecmkD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-04tGhssq3eUecmkD :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}适用适用适用推荐Netty拆包方案固定长度分隔符长度字段固定数据场景文本协议二进制协议金融/游戏/物联网
选择指南:
🔥 终极忠告:生产环境首选LengthFieldBasedFrameDecoder!
动手挑战:
💻 使用Netty实现一个支持文件传输的协议,完美解决大文件拆包问题
评论前必须登录!
注册