Netty从0到1系列之Netty整体架构、入门程序

发布于:2025-09-06 ⋅ 阅读:(17) ⋅ 点赞:(0)

  • 先建立整体认知,不必追求细节.
  • 先建立整体认知,不必追求细节.
  • 先建立整体认知,不必追求细节.

一、Netty概述

1.1 官网资料

https://netty.io/

Netty: Home

Netty 是一个异步事件驱动的网络应用框架
用于快速开发可维护的高性能协议服务器和客户端。

1.2 概述

Netty 是一个 NIO 客户端服务器框架,可以快速轻松地开发网络应用程序,例如协议服务器和客户端。它极大地简化和简化了网络编程,例如 TCP 和 UDP 套接字服务器。

“快速简单”并不意味着生成的应用程序将受到可维护性或性能问题的影响。Netty 是根据从许多协议(如 FTP、SMTP、HTTP 以及各种基于二进制和文本的遗留协议)的实施中获得的经验精心设计的。因此,Netty 成功地找到了一种方法,可以在不妥协的情况下实现开发的便利性、性能、稳定性和灵活性。

在这里插入图片描述

1.3 为什么选择Netty?

Netty 是一个基于 Java NIO 的 异步事件驱动 的高性能网络应用框架,广泛应用于构建高并发、低延迟的协议服务器与客户端,如 RPC 框架(Dubbo、gRPC)、消息中间件(RocketMQ)、即时通讯系统等。

❌ 原生 NIO 编程的复杂性

问题 说明
API 复杂 需手动管理 SelectorChannelBuffer 状态
空轮询 Bug JDK NIO 存在 select() 无限返回 0 的问题
内存管理困难 Buffer 分配、释放、泄漏风险高
编解码复杂 协议解析需手动实现粘包/拆包
线程模型复杂 Reactor 模式需自行实现

🎯 Netty 的目标:简化网络编程,屏蔽底层复杂性,提供高性能、高可扩展性的 API

Netty 核心优势
高性能
高并发
高可扩展
易用性
社区生态
零拷贝支持
内存池
无锁串行化
Reactor 线程模型
支持百万级连接
ChannelHandler 管道
支持多种协议
API 简洁
编解码器丰富
活跃社区
被主流框架采用

✅ 优点

优点 说明
高性能 无锁串行化、内存池、零拷贝
高并发 支持数十万甚至百万连接
高可扩展 ChannelHandler 管道模式,易于扩展
API 简洁 封装了 NIO 复杂性
协议丰富 支持 HTTP、WebSocket、SSL/TLS 等
社区活跃 文档齐全,问题易解决
被广泛采用 Dubbo、RocketMQ、Elasticsearch 等

二、Netty的I/O模型

2.1 I/O模型的核心思想

✅ 核心目标

  • 单线程处理多个连接(高并发)
  • 无阻塞 I/O 操作(低延迟)
  • 事件驱动处理(高响应性)
  • 避免锁竞争(高性能)

🎯 设计哲学

“让一个线程负责一个事件循环,避免上下文切换和锁开销。”

2.2 Netty 的 I/O 模型演进:三种 Reactor 模式

Netty 支持三种经典的 Reactor 模型,可通过配置灵活切换。

2.2.1 单 Reactor 单线程模型(Simple)

单线程
accept
read/write
handle
Channel
Main Reactor
  • 特点:
    • 所有操作(连接、读、写、业务)由同一个线程完成
    • 适合低并发、调试场景
EventLoopGroup group = new NioEventLoopGroup(1);
bootstrap.group(group) // 单线程

⚠️ 不推荐用于生产环境:业务处理阻塞会拖慢整个 I/O 线程。

2.2.2 单 Reactor 多线程模型(Moderate)

accept
Main Reactor Thread
Client Channel
Sub Reactor Group
Worker Thread-1
Worker Thread-2
Worker Thread-N
Handle I/O + 业务
  • 特点

    • Main Reactor:专门处理 accept 新连接
    • Sub Reactor:多个线程处理 I/O 读写
    • 业务逻辑仍在 I/O 线程中执行
  • 适用场景:中等并发,业务处理轻量

⚠️ 风险:若业务处理耗时,仍会阻塞 I/O 线程。

2.2.3 主从 Reactor 多线程模型(Recommended)✅

accept
Boss Group
New Connection
Worker Group
EventLoop-1
EventLoop-2
EventLoop-N
Handle I/O Events
Business Thread Pool
Execute Business Logic
  • 特点:

    • Boss Group:1 个或少量线程,只负责接收连接
    • Worker Group:多个线程,只负责 I/O 读写
    • 业务处理通过 ctx.executor().execute() 提交到独立的业务线程池
  • 优势:

    • I/O 线程永不阻塞
    • 最大化吞吐量
    • 生产环境推荐模式

2.3 Netty I/O 模型的底层优化

✅ 1. 修复 JDK NIO 空轮询 Bug

  • JDK NIO 存在 select() 无限返回 0 的问题
  • Netty 通过 计数检测 + 重建 Selector 修复

✅ 2. 无锁串行化设计

  • 每个 Channel 绑定到固定 EventLoop
  • 所有操作由同一线程执行,无需加锁

✅ 3. 任务调度优化

  • taskQueue:普通任务(如 channel.write()
  • scheduledTaskQueue:定时任务(如心跳)
  • 避免 ScheduledExecutorService 的锁竞争

2.4 I/O 模型对比总结

模型 优点 缺点 适用场景
单 Reactor 单线程 简单、无锁 并发低、易阻塞 学习、调试
单 Reactor 多线程 并发提升 业务阻塞 I/O 线程 中低并发
主从 Reactor 多线程 高并发、高吞吐 稍复杂 生产环境推荐

2.5 Netty I/O 模型的核心价值

维度 说明
核心模式 主从 Reactor 多线程模型
关键优势 异步、非阻塞、无锁、高并发
性能保障 事件循环 + 任务队列 + 内存池
设计精髓 “一个线程一个事件循环”
适用场景 高性能 RPC、网关、消息系统

2.6 📕一句话总结

💡 一句话总结

Netty 的 I/O 模型通过 主从 Reactor 架构无锁串行化设计,实现了 单机百万级并发连接 的能力,是现代高性能网络服务的标准范式

三、Netty整体架构解析

3.1 整体架构

在这里插入图片描述

  • Core层

    • Core 核心层是 Netty 最精华的内容,它提供了底层网络通信的通用抽象和实现,包括可扩展的事件模型、通用的通信 API、支持零拷贝的 ByteBuf 等
  • Protocol Support 协议支持层

    • 协议支持层基本上覆盖了主流协议的编解码实现,如 HTTP、SSL、Protobuf、压缩、大文件传输、WebSocket、文本、二进制等主流协议,此外 Netty 还支持自定义应用层协议。Netty 丰富的协议支持降低了用户的开发成本,基于 Netty 我们可以快速开发 HTTP、WebSocket 等服务。
  • Transport Service 传输服务层

    • 传输服务层提供了网络传输能力的定义和实现方法。它支持 Socket、HTTP 隧道、虚拟机管道等传输方式。Netty 对 TCP、UDP 等数据传输做了抽象和封装,用户可以更聚焦在业务逻辑实现上,而不必关系底层数据传输的细节。

      Netty 的模块设计具备较高的通用性和可扩展性,它不仅是一个优秀的网络框架,还可以作为网络编程的工具箱。Netty 的设计理念非常优雅,值得我们学习借鉴。

3.2 架构设计

Netty 不仅仅是一个网络通信库,它是一个高度模块化、可扩展、高性能的异步事件驱动网络应用框架。其架构设计融合了多种经典设计模式与系统优化思想,是现代高性能网络编程的典范。

本文将从 分层架构、核心组件、数据流、线程模型、扩展机制 五个维度,全面深入剖析 Netty 的整体架构设计。

3.2.1 Netty架构全景图

Utility
CodecAndProtocol
TransportLayer
CoreFramework
ApplicationLayer
内存池
Future/Promise
Bootstrap
编解码器
HTTP
WebSocket
Protobuf
NIO
OIO
Epoll
UDT
ChannelPipeline
Channel
Unsafe
ByteBuf
EventLoop
EventLoopGroup
Thread
ChannelHandler
业务逻辑

3.2.2 Netty的分层架构设计

Netty 采用清晰的 分层架构,各层职责分明,耦合度低。

1. 应用层(Application Layer)

  • 职责:实现具体业务逻辑
  • 组件ChannelHandlerChannelInboundHandlerChannelOutboundHandler

  • 特点:开发者主要编写代码的层级

2. 核心框架层(Core Framework)

  • 职责:事件调度、I/O 处理、线程管理
  • 组件
    • ChannelPipeline:事件处理管道
    • Channel:网络连接抽象
    • EventLoop:事件循环
    • EventLoopGroup:线程组
    • ByteBuf:内存管理

3. 传输层(Transport Layer)

  • 职责:提供多种 I/O 模型支持
  • 实现
    • NIO:基于 JDK NIO(NioEventLoopGroup
    • Epoll:Linux 专属高性能(EpollEventLoopGroup
    • OIO:阻塞 I/O(已不推荐)
    • UDT:UDP-based Data Transfer

4. 协议编解码层(Codec & Protocol)

  • 职责:协议解析与生成
  • 组件
    • ByteToMessageDecoder:字节流 → 消息对象
    • MessageToByteEncoder:消息对象 → 字节流
    • 内置编解码器:LengthFieldBasedFrameDecoderStringEncoder
    • 协议支持:HTTP、WebSocket、SSL/TLS、Protobuf

5. 工具层(Utility Layer)

  • 职责:提供通用能力
  • 组件
    • ByteBufAllocator:内存分配器(支持池化)
    • Future / Promise:异步结果通知
    • Bootstrap / ServerBootstrap:启动辅助类
    • ReferenceCountUtil:引用计数工具

3.3 Netty架构设计精髓总结

设计原则 说明 实现方式
单一职责 每个组件只做一件事 ChannelPipelineEventLoop 分离
开闭原则 对扩展开放,对修改关闭 ChannelHandler 管道模式
依赖倒置 高层依赖抽象 Channel 接口,多种实现(NIO/Epoll)
无锁设计 避免锁竞争 无锁串行化 + EventLoop 绑定 Channel
异步非阻塞 高并发基础 Future/Promise + 事件驱动
资源复用 减少 GC 内存池、对象池

3.4 Netty架构的优缺点分析

✅ 优点

优点 说明
高性能 无锁串行化、内存池、零拷贝
高可扩展 ChannelHandler 可插拔
高可靠性 成熟稳定,被主流框架广泛采用
协议丰富 内置 HTTP、WebSocket、SSL 等
跨平台 支持 NIO(通用)、Epoll(Linux 优化)
易测试 EmbeddedChannel 支持单元测试

❌ 缺点

缺点 说明
学习成本高 Reactor 模型、事件驱动需理解
内存管理复杂 ByteBuf 需手动释放,易泄漏
调试困难 异步堆栈不直观
过度设计 简单场景可能不必要

3.5 Netty架构设计的核心价值

Netty 的架构设计是“简单、高效、可扩展”哲学的完美体现。

维度 说明
核心思想 异步事件驱动 + 无锁串行化 + 责任链模式
关键技术 Reactor 模型、内存池、零拷贝、引用计数
设计目标 单机百万连接、微秒级延迟、高吞吐量
行业地位 Java 高性能网络编程的事实标准
适用场景 RPC、网关、消息系统、游戏服务器、物联网

3.6 一句话总结

💡 一句话总结

Netty 通过 分层架构组件化设计极致优化,构建了一个高性能、高可靠、高可扩展的网络通信平台,是现代分布式系统不可或缺的基础设施。

四、Netty开发环境搭建

4.1 引入依赖

<properties>
    <maven.compiler.source>24</maven.compiler.source>
    <maven.compiler.target>24</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.5.18</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.16</version>
    </dependency>

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.5.18</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>4.1.112.Final</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.38</version>
    </dependency>
</dependencies>

4.2 配置logback.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration
        xmlns="http://ch.qos.logback/xml/ns/logback"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback logback.xsd">
    <!-- 输出控制,格式控制-->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date{HH:mm:ss} [%-5level] [%thread] %logger{17} - %m%n </pattern>
        </encoder>
    </appender>


    <!-- 用来控制查看那个类的日志内容(对mybatis name 代表命名空间) -->
    <logger name="cn.tcmeta" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>

    <logger name="io.netty.handler.logging.LoggingHandler" level="DEBUG" additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>

    <root level="ERROR">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

五、入门程序

5.1 服务器端

package cn.tcmeta.demo01;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import lombok.extern.slf4j.Slf4j;

/**
 * @author: laoren
 * @description: Netty入门案例
 * @version: 1.0.0
 */
@Slf4j
public class NettyServerDemo01 {
    public static void main(String[] args) {
        // 1. 创建服务器
        new ServerBootstrap()
                // 2. 创建NioEventLoopGroup, 现在可以简单理解为: 线程池 + Selector.
                // BossEventLoop, WorkerEventLoop(select, thread)
                .group(new NioEventLoopGroup())
                // 3. 选择服务Socket实现类, 其中NioServerSocketChannel, 表示NIO的服务器端实现.
                .channel(NioServerSocketChannel.class)
                // 4. 接下来添加的处理器都是给SocketChannel用的, 而不是给ServetSocketChannel用的.
                // ChannelInitializer处理器(仅执行一次),它的作用是待客户端SocketChannel建立连接后, 执行
                // initChannel以便添加更多的处理器.
                // boss处理连接, child(worker)进行读写操作.所以这里child添加的是处理SocketChannel的主要逻辑.
                // 执行操作封装为handler(处理器)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    // 5. 执行添加处理器
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception { // ChannelInitializer, 初始化channel, 添加各种各样的handler
                        // 7. 处理SocketChannel, 解码 ByteBuf ---> String
                        ch.pipeline().addLast(new StringDecoder());
                        // 8. 处理SocketChannel业务处理器, 使用上一个处理器的处理结果.
                        ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
                            @Override
                            protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                                System.out.println(msg);
                            }
                        });
                    }
                    // 6. 绑定服务器端口号
                }).bind(8888);

        log.debug("服务器已经启动, 监听端口号为 8888 ................");
    }
}

5.2 客户端

package cn.tcmeta.demo01;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;

import java.util.Date;

/**
 * @author: laoren
 * @description: 客户端测试
 * @version: 1.0.0
 */
public class ClientDemo01 {
    public static void main(String[] args) throws InterruptedException {
        new Bootstrap()
                // 创建NioEventLoopGroup, 可以理解为线程池 + Selector
                .group(new NioEventLoopGroup())
                // 2.
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<Channel>() {
                    @Override
                    protected void initChannel(Channel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder()); // 设置编码器
                    }
                })
                .connect("127.0.0.1", 8888)
                .sync()
                .channel()
                // 向服务器发送数据
                .writeAndFlush(new Date() + ", hello netty!");
    }
}

服务器输出日志

在这里插入图片描述

5.3 程序解析

  • channel可以理解为数据传输的通道.
  • msg理解为流动的数据, 原始数据是ByteBuf, 经过pipeline的加工, 会将其变成类型对象, 最后输出又变成ByteBuf对象.
  • handler理解为数据处理的工序
    • 工序有多道,合在一起就构成了pipeline, pipeline负责发布事件【读、取完成等】传播每个handler, handler对自己感兴趣的事件进行处理【重写相应事件处理的方法】
    • handler分为InboundOutBound两类.
  • eventLoop理解为处理数据的工人
    • 工人可以同时管理多个channelio操作,并且一旦工作负责了某个channel,就要负责到底【绑定】
    • 工人既可以执行 io 操作,也可以进行任务处理,每位工人有任务队列,队列里可以堆放多个 channel 的待处理任务,任务分为普通任务、定时任务
    • 工人按照 pipeline 顺序,依次按照 handler 的规划(代码)处理数据,可以为每道工序指定不同的工人

网站公告

今日签到

点亮在社区的每一天
去签到