【2024博客之星】我的年度技术总结:Netty渡劫指南--从线程暴走到百万长连接,这一年我踩过的坑比写的代码还多

时间过得真快,作为一名十年的技术老鸟,这一年来跟Netty打交道打得不少。今天就聊聊这一年来我跟Netty的那些事儿,还有我在学习它技术原理时的一些总结。

导读

    • Netty再相见:捡起来、用起来
    • Netty原理学习:边啃边写变总结
    • Netty实战:干不爬我的终将被我干爬
      • 一、 为什么选择Netty?
      • 二、 线程模型:从车祸现场到秋名山车神
      • 三、 内存管理:从OOM拳皇到内存刺客
      • 四、 协议设计:从二进制乱码到量子通信
      • 五、 性能调优:从青铜到王者的九重天劫
      • 六、 填坑实录:那些让我掉头发的灵异事件
      • 七、 我们离完美还有多远?

Netty再相见:捡起来、用起来

记得刚开始接触Netty那会儿,大概10年前吧,技术很菜, 看的很浅,纯纯的是为了学习。但是呢, 10年前的东西早就还了回去, 好巧不巧, 今年接到一个技术重构项目需要用到netty,于是不得不重新开始学起来…当然这次要啃啃源码…

Netty这个异步事件驱动的网络应用框架,听起来挺高大上的,实际它就是帮我们封装好了Java NIO的那些底层细节,让我们能更专心地写业务逻辑,不用跟那些复杂的IO操作较劲。开始,看着Netty那一堆的API和组件,也比较乱。不过呢,Netty的文档还算齐全,社区也挺活跃的,一边查文档,一边看源码,慢慢地,也就上手了。

其实用了Netty一段时间之后,你会发现Netty的设计思路特别清晰,用起来也特别顺手。比如说,Netty的Pipeline和Handler机制。就像流水线和工人,每个Handler都可以对经过的事件进行处理或拦截。这种设计让Netty在处理网络事件时特别灵活,扩展性也强。

Netty原理学习:边啃边写变总结

当然要用好Netty,光知道怎么用可不行,还得知道它的技术原理。比如说,Netty的Reactor线程模型。Reactor模式是一种事件驱动的设计模式,特别适合处理并发IO操作。Netty通过实现多种Reactor模式,来适应不同的应用场景和需求。还有啊,Netty通过Direct Buffer、FileRegion等组件实现了零拷贝,从而大大提高了数据传输的性能。

边学源码, 边写写博客,当然写作质量我们另外考量…

netty源码解读:https://blog.csdn.net/qq_26664043/category_12729336.html

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


说了这么多,还是得拿实战来说话。


Netty实战:干不爬我的终将被我干爬

2024年的某个下午业务高峰期,监控大屏突然飙红——公司智慧物流平台的服务端像吃了泻药般疯狂Full GC。看着每秒10万+的物流轨迹数据在Kafka堆积成山,我握着保温杯的手微微颤抖:“Netty啊Netty,说好的’高性能异步框架’呢?” 这魔幻一幕,正是兄弟我与Netty年度缩影。


一、 为什么选择Netty?

年中接手物流中台项目时,大哥(leader)拉着我们聊聊技术:“单机支撑10万长连接,延迟不超过300ms”。那一刻仿佛听见了Spring WebFlux在角落里哭泣。

传统BIO的死亡现场

  • 当模拟5000客户端压测时,Tomcat线程池直接罢工,日志里满是"RejectedExecutionException"
  • 同步阻塞模型下,每个请求都像在早高峰挤地铁——明明已经到站了,就是下不去车

Netty的三大绝活

  1. 事件驱动模型:就像有个AI交警指挥交通,一个线程能处理N个路口(Channel)
  2. 零拷贝黑科技:FileRegion+CompositeByteBuf组合拳,内存复制开销直降70%
  3. 内存池化技术:ByteBuf的Arena分配策略,让JVM不再表演"内存过山车"

当第一个原型系统跑通时,资源监控显示:

# 传统BIO
Memory: 4G/8G (50%) 
Threads: 1500+(瑟瑟发抖)

# Netty
Memory: 1.2G/8G (15%)
Threads: 36(CPU核数x2) 

那一刻,大哥露出了"地主家有余粮"的微笑。


二、 线程模型:从车祸现场到秋名山车神

你以为用了Netty就能高枕无忧?Too young!第一次压测时,QPS刚到8000就触发了线程死锁,日志里堆栈信息直接把人干崩溃,程序员的崩溃…

经典翻车现场

// 错误示范:在IO线程执行数据库操作
channel.pipeline().addLast(new SimpleChannelInboundHandler<ByteBuf>() {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
        // 在EventLoop线程执行JDBC查询 → 直接阻塞IO线程!
        userDao.query(msg.toString()); 
    }
});

涅槃重生的线程架构

+---------------------+
| BossGroup (NioEventLoop) 
| 处理Accept事件        |
+----------+----------+
           |
           v
+---------------------+
| WorkerGroup (NioEventLoop × N)
| 处理IO读写           |
+----------+----------+
           |
           v
+---------------------+
| BusinessThreadPool 
| 自定义业务线程池       | ← 这里才是处理慢操作的地方
+---------------------+

代码救赎之路

// 使用额外的业务线程池
ExecutorService businessExecutor = Executors.newFixedThreadPool(32);

channel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        businessExecutor.execute(() -> {
            // 把耗时操作扔到业务线程池
            processBusinessLogic(msg);
            ctx.writeAndFlush(response);
        });
    }
});

调整后系统吞吐量直冲5万QPS,深藏功与名。


三、 内存管理:从OOM拳皇到内存刺客

某次生产环境半夜告警:“Heap usage超过90%”。打开MAT分析堆dump,发现DirectByteBuffer疯长——原来某个ChannelHandler忘记release ByteBuf了。

填坑四部曲

  1. 开启内存泄露检测(代价是性能下降30%,仅调试时用)
// 启动参数添加
-Dio.netty.leakDetection.level=PARANOID
  1. 对象池化改造
// 复用ByteBuf对象
private static final Recycler<ByteBuf> RECYCLER = new Recycler<>() {
    protected ByteBuf newObject(Handle<ByteBuf> handle) {
        return UnpooledByteBufAllocator.DEFAULT.buffer(1024).retain();
    }
};

ByteBuf buf = RECYCLER.get();
try {
    // 业务操作...
} finally {
    buf.release(); // 放回池中
}
  1. 堆外内存限流
// 防止DirectMemory耗尽
-Dio.netty.maxDirectMemory=2g
  1. 精准狙击内存泄漏
// 继承SimpleChannelInboundHandler自动释放
public class MyHandler extends SimpleChannelInboundHandler<ByteBuf> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
        // 无需手动release
    }
}

改造后内存波动曲线从"过山车"变成了"老(年)太极"。


四、 协议设计:从二进制乱码到量子通信

通信协议文档写着:“第3字节为状态位,0x01代表正常”。结果真实数据里该位置突然出现0x03,有兄弟幽幽地说:“哦,我们上周刚加了’振动异常’状态…”

协议层防崩溃设计

  1. 魔数校验(快速过滤无效连接)
// 协议头校验
if (msg.getInt(0) != 0xDEADBEEF) {
    ctx.close();
    return;
}
  1. 动态协议适配
// 根据版本号选择解码器
int version = msg.getByte(4);
switch (version) {
    case 1 -> pipeline.addLast(new V1Decoder());
    case 2 -> pipeline.addLast(new V2Decoder());
    default -> throw new UnsupportedProtocolException();
}
  1. 柔性降级策略
// 使用Protobuf的扩展字段
message BasePacket {
    required int32 type = 1;
    extensions 1000 to max; // 为未来字段留空间
}

这套机制成功扛住了3次协议变更。为什么频繁变更,懂的你都懂…


五、 性能调优:从青铜到王者的九重天劫

当技术总监要求"百万连接不卡顿"时,我知道真正的战斗开始了。以下是血泪换来的调优圣经:

调优参数表

参数默认值优化值效果
SO_BACKLOG102432768半连接队列扩容
TCP_NODELAYfalsetrue禁用Nagle算法降延迟
SO_RCVBUF/SO_SNDBUF系统默认1MB避免小包频繁传输
ALLOCATORPooledUnpooled根据场景选择(高并发用池化)
WRITE_BUFFER_WATER_MARK32KB-64KB1MB-2MB大流量防写阻塞

JVM黄金搭档

-server 
-Xmx4g -Xms4g 
-XX:+UseG1GC 
-XX:MaxGCPauseMillis=100 
-XX:InitiatingHeapOccupancyPercent=35

Linux内核黑魔法

# 调整最大文件描述符
ulimit -n 1000000

# 优化TCP参数
sysctl -w net.core.somaxconn=32768
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_fin_timeout=15

经过这番操作,单机长连接从5万飙到十几万,GC时间从800ms/次降到60ms。


六、 填坑实录:那些让我掉头发的灵异事件

  1. EPOLL空轮询BUG
    现象:CPU突然100%且持续不退
    解法:升级Netty到4.1.68+,或设置-Dio.netty.noKeySetOptimization=true

  2. ChannelHandler的暗黑生命周期
    掉坑:在handlerAdded()里写业务逻辑导致死锁
    忠告:记住Handler的调用链是"先add后init",别乱搞状态

  3. WriteAndFlush的量子纠缠
    经典错误:

    ctx.write(buffer1);
    ctx.write(buffer2);
    ctx.flush(); // 这里flush的只有buffer1!
    

    正确姿势:用ChannelFuture future = ctx.write(buffer1).write(buffer2).flush();

  4. 空闲检测的狼来了
    误判设备离线?原来是心跳间隔设置不合理:

    // 服务端设置读空闲60秒
    pipeline.addLast(new IdleStateHandler(60, 0, 0)); 
    // 客户端设置写空闲30秒
    pipeline.addLast(new IdleStateHandler(0, 30, 0)); 
    

七、 我们离完美还有多远?

站在2025年的起点,Netty生态又有了新变化:

  • GraalVM原生镜像:启动时间从3秒缩短到0.3秒
  • QUIC协议支持:HTTP/3的UDP传输层已进入试验阶段
  • AI智能调控:基于LSTM预测流量波峰,动态调整线程池
  • 混沌工程防护:自动模拟网络抖动、包乱序等极端场景

回望这一年的技术长征,Netty就像一把杀猪刀——初见平平无奇,深究方知精妙。正如Netty之父Trustin Lee所说:“The performance is not an accident, it’s a design.” 或许这就是技术的魅力:用精心设计的架构,在比特洪流中搭建起秩序的方舟。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/956741.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Tomcat下载配置

目录 Win下载安装 Mac下载安装配置 Win 下载 直接从官网下载https://tomcat.apache.org/download-10.cgi 在圈住的位置点击下载自己想要的版本 根据自己电脑下载64位或32位zip版本 安装 Tomcat是绿色版,直接解压到自己想放的位置即可 Mac 下载 官网 https://tomcat.ap…

【CSDN博客之星2024】主题创作《总结2024,为了遇见更好的2025》

【博客之星2024】主题创作《总结2024&#xff0c;为了更好的2025》 一、AI技术变革日新月异二、总结我的CSDN2024三、技术深耕&#xff0c;从实践中汲取力量3.1、在数据库技术方面3.2、在javavue前后端开发领域3.3、在项目运维领域3.4、在GIS开发方面 四、2025工作计划五、2025…

MySQL 事务及MVCC机制详解

目录 什么是事务 事务的隔离级别 数据库并发的三种场景 读-写 什么是事务 事务就是一组DML语句组成&#xff0c;这些语句在逻辑上存在相关性&#xff0c;这一组DML语句要么全部成功&#xff0c;要么全部失败&#xff0c;是一个整体。MySQL提供一种机制&#xff0c;保证我们…

数据库存储上下标符号,sqlserver 2008r2,dm8

sqlserver 2008r2&#xff1a; 数据类型需要用nvarchar插入数据时字符串前需要用N create table test( col1 varchar(50), col2 nvarchar(50) ) insert into test(col1,col2) values(U⁴⁵⁶⁷⁸⁹⁰D₁₂₃₄₅₆₇₈₉₀,U⁴⁵⁶⁷⁸⁹⁰D₁₂₃₄₅₆₇₈₉₀) insert into…

Java高频面试之SE-15

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本牛马baby今天又来了&#xff01;哈哈哈哈哈嗝&#x1f436; String 怎么转成 Integer 的&#xff1f;它的原理是&#xff1f; 在 Java 中&#xff0c;要将 String 转换为 Integer 类型&#xff0c;可…

nacos2.3.0 接入pgsql或其他数据库

首先尝试使用官方插件进行扩展&#xff0c;各种报错后放弃&#xff0c;不如自己修改源码吧。 一、官方解决方案 1、nocos 文档地址&#xff1a;Nacos 配置中心简介, Nacos 是什么 | Nacos 官网 2、官方解答&#xff1a;nacos支持postgresql数据库吗 | Nacos 官网 3、源码下载地…

城市电动出行的智慧升级:充电桩可视化管理

通过图扑可视化管理平台&#xff0c;实时监控与优化城市充电桩网络&#xff0c;提高运维效率与用户满意度&#xff0c;支撑绿色交通体系发展&#xff0c;为电动出行打造更加智能化的基础设施解决方案。

关于 Cursor 的一些学习记录

文章目录 1. 写在最前面2. Prompt Design2.1 Priompt v0.1&#xff1a;提示设计库的首次尝试2.2 注意事项 3. 了解 Cursor 的 AI 功能3.1 问题3.2 答案 4. cursor 免费功能体验5. 写在最后面6. 参考资料 1. 写在最前面 本文整理了一些学习 Cursor 过程中读到的或者发现的感兴趣…

idea中远程调试中配置的参数说明

Ⅰ 远程调试中配置的端口号与服务本身端口号区别 一、远程调试中配置端口号的作用 在 IDEA 中进行远程调试时配置的端口号主要用于建立开发工具&#xff08;如 IDEA&#xff09;和远程服务之间的调试连接。当你启动远程调试时&#xff0c;IDEA 会监听这个配置的端口号&#xf…

基于 MDL 行情插件的中金所 L1 数据处理最佳实践

本文介绍了如何通过 DolphinDB 的 MDL 插件订阅并处理中金所 Level 1 实时数据。首先&#xff0c;文章简要介绍了 MDL 插件的功能和作用。它是基于 MDL 官方提供的行情数据服务 C SDK&#xff08;即 TCP 版本 MDL &#xff09;实现&#xff0c;提供了实时数据获取和处理的能力。…

JupyterLab 安装以及部分相关配置

安装 JupyterLab pip install jupyter启动 JupyterLab jupyter lab [--port <指定的端口号>] [--no-browser] # --port 指定端口 # --no-browser 启动时不打开浏览器安装中文 首先安装中文包 pip install jupyterlab-language-pack-zh-CN安装完成后重启 JupyterLab 选…

LabVIEW电源纹波补偿

在电子设备的电源管理中&#xff0c;电源纹波的存在可能会对设备的稳定性和性能产生负面影响。以某精密电子仪器的电源纹波补偿为例&#xff0c;详细阐述如何运用 LabVIEW 编写程序进行电源纹波补偿。将从电源纹波特点、测量采样、滤波、反馈控制等多个方面展开介绍。 ​ 电源…

嵌入式硬件篇---基本组合逻辑电路

文章目录 前言基本逻辑门电路1.与门&#xff08;AND Gate&#xff09;2.或门&#xff08;OR Gate&#xff09;3.非门&#xff08;NOT Gate&#xff09;4.与非门&#xff08;NAND Gate&#xff09;5.或非门&#xff08;NOR Gate&#xff09;6.异或门&#xff08;XOR Gate&#x…

使用rpc绕过咸鱼sign校验

案例网站是咸鱼 找到加密函数i()&#xff0c;发现参数是由token时间戳appkeydata构成的 js客户端服务 考虑到网站可能有判断时间戳长短而让请求包失效的可能&#xff0c;我们请求包就直接用它的方法生成 下面我们先把token和h置为键值对tjh123 再把方法i()设为全局变量my_…

鸿蒙安装HAP时提示“code:9568344 error: install parse profile prop check error” 问题现象

在启动调试或运行应用/服务时&#xff0c;安装HAP出现错误&#xff0c;提示“error: install parse profile prop check error”错误信息。 解决措施 该问题可能是由于应用使用了应用特权&#xff0c;但应用的签名文件发生变化后未将新的签名指纹重新配置到设备的特权管控白名…

Pix2Pix :用于图像到图像转换的条件生成对抗网络

1. 背景与问题 图像到图像的转换&#xff08;Image-to-Image Translation&#xff09;是计算机视觉中的一个重要任务&#xff0c;指的是在输入一张图像的情况下&#xff0c;生成一张风格、内容或其他条件不同但语义一致的图像。随着深度学习的发展&#xff0c;尤其是生成对抗网…

【大数据2025】Hadoop 万字讲解

文章目录 一、大数据通识大数据诞生背景与基本概念大数据技术定义与特征大数据生态架构概述数据存储数据计算与易用性框架分布式协调服务和任务调度组件数仓架构流处理架构 二、HDFSHDFS 原理总结一、系统架构二、存储机制三、数据写入流程四、心跳机制与集群管理 安全模式&…

docker 安装 nanomq

1.拉取镜像 docker pull emqx/nanomq:latest 2. 创建配置文件夹&#xff08;示例放在/home/nanomq&#xff09; mkdir nanomq chomd 777 nanomq3. 创建配置文件&#xff08;nanomq.conf&#xff09;写入以下内容 mqtt {property_size 32max_packet_size 10KBmax_mqueue_le…

【STM32G4xx的CAN驱动记录】

STM32G4xx的CAN驱动记录 CAN说明CAN的波特率计算数据测试总结 本文主要记录了基于STM32G4xx的CAN接口解析某型号雷达数据遇到的问题及规避方法&#xff0c;CAN总线波特率500Kbps&#xff0c;采样点要求80%附近。 注意CAN总线同步段的时间&#xff01;&#xff01;&#xff01; …

2024年CSDN博客之旅:成长、创作与生活的交响曲

文章目录 《2024年博客之旅&#xff1a;成长、创作与生活的交响曲》一、引言二、个人成长与突破盘点&#xff08;一&#xff09;技术能力的提升&#xff08;二&#xff09;解决问题能力的增强&#xff08;三&#xff09;沟通与表达能力的进步 三、年度创作历程回顾&#xff08;…