十大组件

Bootstrap / ServerBootstrap 启动器 / 服务端启动器

EventLoop / EventLoopGroup 事件循环/事件循环组

Channel 通道

ChannelPipeline 管道

ChannelHandler 处理器

ChannelHandlerContext 处理器上下文

ChannelFuture 异步结果

ByteBuf 字节缓冲区

概述

Bootstrap / ServerBootstrap 负责将 Channel、EventLoopGroup、ChannelPipeline、ChannelHandler 以及相关配置组合并启动。
EventLoop 是负责 IO 事件和任务执行的单线程执行器,一个 EventLoop 可以管理多个 Channel,而一个 Channel 在生命周期内只绑定一个 EventLoop。EventLoopGroup 是管理多个 EventLoop 的线程池。
每个 Channel 都绑定一个 ChannelPipeline,Pipeline 由多个 ChannelHandlerContext 组成,每个 ChannelHandlerContext 对应一个 ChannelHandler。
当 IO 事件在 Channel 上发生时,事件会沿着 ChannelPipeline 传播,由其中的 ChannelHandler 依次进行处理或拦截。
ChannelHandlerContext 表示当前 Handler 在 Pipeline 中的操作入口和事件传播起点,可用于继续传递事件、写数据以及访问 Channel 和 EventLoop。
一个 Channel 可以产生多个 ChannelFuture,每个 ChannelFuture 只对应一次具体的异步 Channel 操作,用于表示该操作的完成状态(未完成 / 成功 / 失败)。
ByteBuf 是 Netty 中用于网络 IO 的高性能字节容器,负责数据存储、读写指针和内存管理,通常在 Handler 中被读取和处理,用于替代 ByteBuffer。

Bootstrap

EventLoopGroup group = new NioEventLoopGroup();

Bootstrap bootstrap = new Bootstrap();
bootstrap
    //客户端 只需要一个 EventLoopGroup
	//负责 connect、read/write、回调handler
    .group(group)
    //指定用什么Channel 实现
    .channel(NioSocketChannel.class)
    //客户端只有 handler,没有 childHandler
    //为这个Channel初始化并绑定它的ChannelPipeline和其中的 ChannelHandler。
    .handler(new ChannelInitializer<SocketChannel>() {
        @Override
        protected void initChannel(SocketChannel ch) {
            ch.pipeline().addLast(new ClientHandler());
        }
    });
//连接服务器
ChannelFuture future = bootstrap.connect("127.0.0.1", 8080).sync();

ServerBootstrap

EventLoopGroup bossGroup = new NioEventLoopGroup(1);   // 接收连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 处理 IO

try {
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap
        // 1. 绑定 boss / worker 线程模型
        .group(bossGroup, workerGroup)
        // 2. 指定服务端 Channel(监听端口用)
        .channel(NioServerSocketChannel.class)
        // 3. (可选)ServerChannel 的 handler
        .handler(new LoggingHandler(LogLevel.INFO))
        // 4. 给“每一个新连接”的 Channel 初始化 Pipeline
        .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) {
                ch.pipeline().addLast(
                    new ServerHandler()
                );
            }
        })
        // 5. TCP 参数(给子连接用)
        .childOption(ChannelOption.SO_KEEPALIVE, true);
    // 6. 绑定端口(异步)
    ChannelFuture future = bootstrap.bind(8080).sync();
    // 7. 阻塞等待服务端关闭
    future.channel().closeFuture().sync();
} finally {
    // 8. 优雅关闭线程池
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
}

EventLoop/EventLoopGroup

EventLoopGroup:负责创建和管理多个 EventLoop,并在 Channel 注册时为每个 Channel 分配一个 EventLoop
EventLoop:是单线程事件循环,负责监听 IO 就绪并执行对应的 Channel 处理逻辑。Netty把EventLoop藏起来了

// 服务端
// bossGroup:只负责 accept
// 指定分配1个线程
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// workerGroup:负责读写 IO
// 线程数 = CPU 核数 * 2
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap
    .group(bossGroup, workerGroup)  // 服务端专用写法
    .channel(NioServerSocketChannel.class);

// 客户端
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap
    .group(group)   // 客户端只有一个 EventLoopGroup
    .channel(NioSocketChannel.class);

// 执行普通任务(重点)
group.execute(() -> {
    // 这个任务会在某个 EventLoop 线程中执行
    // 把任务放进 Netty 的 IO 线程
    // 保证和 Channel 在同一线程
});

// 优雅关闭
group.shutdownGracefully();

Channel

通道就是“连接对象”,可以是 TCP 连接、UDP 通道或者 JVM 内部通信管道。

指定Channel

常用通道:
TCP 通道:NioServerSocketChannel、NioSocketChannel
Linux 高性能 TCP:EpollServerSocketChannel / EpollSocketChannel

// 企业实现切换epoll模式
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.Channel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;

public class NettyServer {

    public static void main(String[] args) {

        // 判断当前系统是否支持 epoll
        boolean useEpoll = Epoll.isAvailable();

        EventLoopGroup bossGroup;
        EventLoopGroup workerGroup;
        Class<? extends Channel> channelClass;

        if (useEpoll) {
            // Linux + 支持 epoll
            bossGroup = new EpollEventLoopGroup(1);
            workerGroup = new EpollEventLoopGroup();
            channelClass = EpollServerSocketChannel.class;
        } else {
            // Windows / macOS / 不支持 epoll
            bossGroup = new NioEventLoopGroup(1);
            workerGroup = new NioEventLoopGroup();
            channelClass = NioServerSocketChannel.class;
        }

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap
            .group(bossGroup, workerGroup)
            .channel(channelClass);
    }
}

获取Channel

Channel在两个地方使用,在业务 Handler 里,在启动 / 管理代码里。

//在业务 Handler 里获取其Channel
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    Channel channel = ctx.channel();
}

//在启动/管理代码里获取其Channel
//客户段
ChannelFuture future = bootstrap.connect(host, port).sync();
Channel channel = future.channel();
//服务端
ChannelFuture future = serverBootstrap.bind(8080).sync();
Channel serverChannel = future.channel();
//ChannelFuture.channel() 在成功和失败时都存在;
//但只有成功时,这个 Channel 才是“可用的连接”。

使用Channel

//在业务Handler里

//读 / 写业务数据(最核心)
//典型场景:请求 → 响应、推送当前连接的消息、回 ACK / 心跳
ctx.channel().writeAndFlush(response);

//绑定 / 获取业务状态(非常常用)
//用途:登录态、用户 ID、权限信息、协议版本
ctx.channel().attr(USER_ID).set("10086");

//判断连接是否可用
//防止:对已断开的连接写数据、NPE / 无效写
if (ctx.channel().isActive()) {
    ...
}

//关闭当前连接
//典型原因:协议非法、鉴权失败、超过次数限制
ctx.channel().close();

//拿 EventLoop 做线程安全调度
ctx.channel().eventLoop().execute(() -> {
    // 在该 Channel 所属线程执行
});
//在启动 / 管理代码里获取 Channel

//保存 Channel(连接管理)
//用途:多客户端连接、后续主动推送、广播消息
Channel channel = future.channel();
channelGroup.add(channel);

//等待关闭(主线程阻塞)
//用途:服务不退出、Demo / main 方法
//只在 main / 启动代码里用
channel.closeFuture().sync();

//主动关闭连接 / 服务
channel.close();
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();

ChannelPipeline

ChannelPipeline 是绑定在 Channel 上的一条“责任链”,
用来按顺序处理入站(Inbound)和出站(Outbound)事件。

也是有两个地方使用,Pipeline 初始化阶段:装 Handler;Handler 运行阶段:改 Handler 或触发事件。

//初始化阶段(ChannelInitializer 里)

//初始化 Pipeline(最常见)
ch.pipeline().addLast(new ClientHandler());

//按顺序添加多个 Handler
ChannelPipeline p = ch.pipeline();
p.addLast(new Decoder());
p.addLast(new Encoder());
p.addLast(new BusinessHandler());

//带名字的 Handler
p.addLast("decoder", new Decoder());
p.addLast("encoder", new Encoder());
p.addLast("biz", new BusinessHandler());

//运行阶段(Handler 里,通过 ctx 获取)

//(运行时)插入 Handler
p.addAfter("decoder", "auth", new AuthHandler());
p.addBefore("biz", "log", new LogHandler());

//移除 Handler
p.remove("auth");
p.remove(AuthHandler.class);

//替换 Handler
p.replace("biz", "newBiz", new NewBusinessHandler());

ChannelHander

ChannelHandler = ChannelPipeline 中的数据处理器,它负责“处理和响应事件”,包括入站(读)和出站(写)事件。

Inbound 事件包括: 收到数据(channelRead)、 连接建立(channelActive)、 连接断开(channelInactive)、异常(exceptionCaught) 、心跳 / 空闲(userEventTriggered

Outbound 事件包括: 写数据(write)、 刷新(flush) 、连接(connect)、 关闭(close)、 绑定端口(bind

Netty 有 Inbound / Outbound / Duplex 三类 Handler 共用一个 Pipeline;
实际开发中主要自定义 Inbound,Outbound 多由 Encoder 或框架提供,Duplex 只在读写强相关时使用。

直接new的Hander

pipeline.addLast(new IdleStateHandler(60, 0, 0));

//IdleStateHandler(long readerIdleTime, long writerIdleTime, long allIdleTime, TimeUnit unit)
//readerIdleTime – 读空闲时间(单位默认秒,如果没指定 TimeUnit)。
//writerIdleTime – 写空闲时间。
//allIdleTime – 读写都空闲时间。

//当超过指定时间没有读/写操作时,会触发 IdleStateEvent。
//你可以在 pipeline 的下一个 handler 中捕获这个事件,做心跳或者断开连接处理。
//不管是读空闲还是写空闲,都是下一个handler捕获

//处理这个事件例子
//重写ChannelInboundHandlerAdapter里的方法
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    if (evt instanceof IdleStateEvent e) {
        if (e.state() == IdleState.READER_IDLE) {
            System.out.println("读空闲超时,可能客户端断开了");
            ctx.close(); // 可选择断开连接
        }
    } else {
        super.userEventTriggered(ctx, evt);
    }
}
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536));

//HttpServerCodec 负责 HTTP 协议解析/编码
//HttpObjectAggregator 负责 把碎片 HTTP 请求拼成一个完整请求
//两个一起用,你的业务 Handler 才能“像用 Servlet 一样”处理 HTTP

pipeline.addLast(new LengthFieldBasedFrameDecoder(...));
//按长度拆包

pipeline.addLast(new DelimiterBasedFrameDecoder(...))
//按分隔符拆包
pipeline.addLast(sslContext.newHandler(ch.alloc()));
//给当前 Channel 加一个 SSL/TLS Handler,让后续所有通信都变成“加密的 HTTPS / TLS 通信”。
//TCP 还是 TCP
//只是多了一层 SSL/TLS 协议
//所有 读到的数据会先被解密
//所有 写出的数据会先被加密
pipeline.addLast(new LoggingHandler(LogLevel.INFO));
//打日志

最常用的类

//ChannelInboundHandlerAdapter(最常用)
//所有方法都有默认实现
//你只 override 你关心的
//业务开发首选
public class MyHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 处理入站数据
        ctx.fireChannelRead(msg); // 或者自己消费
    }
}

//SimpleChannelInboundHandler<T>(更安全)
//自动释放资源
//类型安全
//非常适合「解码后的业务对象」
//不适合跨 Handler 传 msg 的场景
public class MyHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) {
        // 自动释放 ByteBuf / msg
    }
}

//ChannelDuplexHandler(少量场景)
//用于:读写强相关、连接状态统计、统一日志、监控
public class MyDuplexHandler extends ChannelDuplexHandler {
    @Override
    public void channelRead(...) { }
    @Override
    public void write(...) { }
}

ChannelHandlerContext

ChannelHandlerContext(简称 ctx)是一个 Handler 在 Pipeline 中的“上下文对象”,它连接着当前 Handler、所属 Channel、Pipeline 以及事件传播机制。

ctx 是每个 Handler 专属的上下文,只能在 Handler 内使用它触发事件或操作 Channel;其他地方只能通过 Channel 或 Pipeline 间接操作。

//使用方法

//Inbound 事件(接收数据/异常)
fireChannelRead(msg)	//将消息传给下一个 Inbound Handler
fireChannelReadComplete()	//通知下一个 Handler 数据读取完成
fireExceptionCaught(Throwable cause)	//将异常沿 Inbound 链传递
fireChannelActive()  //触发通道激活事件
fireChannelInactive()	//触发通道失效事件
    
//Outbound 事件(发送数据/控制连接)
write(msg)	//将数据沿 Outbound 链传播(不 flush)
flush()	//刷新写缓冲区,将数据发送到 Socket
writeAndFlush(msg)	//写 + 刷新,常用组合
close()	//关闭 Channel
disconnect()	//断开连接      

ChannelFuture

ChannelFuture 是 Netty 异步 IO 操作的“结果凭证”

三种结果

future.isDone():操作是否完成

future.isSuccess():是否成功

future.cause():如果失败,拿异常

Throwable cause = future.cause();
//返回值类型是Throwable
//如果操作成功 → 返回null
//如果操作失败 → 返回 具体异常对象(IOException、ClosedChannelException 等)

阻塞

Netty 的 IO 是异步的,写、连接、关闭等操作立即返回 ChannelFuture
但有时我们希望阻塞等待操作完成,就需要用 sync()await()

future.sync(); //被中断会抛出InterruptedException 

ChannelFuture future = channel.writeAndFlush(msg).await();
//等待任务结束,如果任务失败,不会抛异常,而是通过 isSuccess 判断
if (future.isSuccess()) {
    // 成功
} else {
    Throwable cause = future.cause(); // 获取异常
}

与 Channel 的关系

特性 描述
一个 Channel 可以有多个 ChannelFuture 每个异步操作(write、connect、bind、close)都会返回一个 ChannelFuture
一个 ChannelFuture 只对应一次操作 比如一次 write、一次 connect
可以通过 ChannelFuture 获取操作对应的 Channel future.channel()

使用场景

绑定端口(ServerBootstrap)

ChannelFuture future = serverBootstrap.bind(8080);
future.addListener(f -> {
    if(f.isSuccess()) {
        System.out.println("端口绑定成功");
    } else {
        System.err.println("端口绑定失败: " + f.cause());
    }
});

发送消息

ChannelFuture writeFuture = ch.writeAndFlush("Hello");
writeFuture.addListener(f -> {
    if(f.isSuccess()) {
        System.out.println("发送成功");
    } else {
        System.err.println("发送失败: " + f.cause());
    }
});

关闭 Channel

ChannelFuture closeFuture = ch.close();
closeFuture.addListener(f -> System.out.println("Channel 已关闭"));

ByteBuf

ByteBuf 是 Netty 提供的高性能字节容器,用于存储、读取和写入网络数据

ByteBuf 主要出现在 ChannelPipeline 中的 Inbound / Outbound Handler 里,用来承载“正在网络中流动的数据”。

指针和容量

[ 0 … readerIndex … writerIndex … capacity ]

readerIndex:当前可读的位置
writerIndex:当前可写的位置
capacity:总容量
readableBytes() = writerIndex - readerIndex
writableBytes() = capacity - writerIndex

常用操作

//读取数据
ByteBuf buf = ...;
int readable = buf.readableBytes();
// 按顺序读取
byte b = buf.readByte();
int i = buf.readInt();
byte[] bytes = new byte[readable];
buf.readBytes(bytes);
//readXXX 会移动 readerIndex


//写入数据
ByteBuf buf = ...;
buf.writeByte(0x01);
buf.writeInt(100);
buf.writeBytes("Hello".getBytes());
// writeXXX 会移动 writerIndex



//不改变指针的访问(get / set)
byte b = buf.getByte(0);  // 不移动 readerIndex
buf.setByte(0, (byte)0xFF); // 不移动 writerIndex


//slice/duplicate(零拷贝)
ByteBuf slice = buf.slice(0, 5); // 引用原始内存,不拷贝;读写指针独立
ByteBuf dup = buf.duplicate();   // 完全复制指针,但共享数据;
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐