Netty从0到1系列之Netty Handler


推荐阅读:

【01】Netty从0到1系列之I/O模型
【02】Netty从0到1系列之NIO
【03】Netty从0到1系列之Selector
【04】Netty从0到1系列之Channel
【05】Netty从0到1系列之Buffer(上)
【06】Netty从0到1系列之Buffer(下)
【07】Netty从0到1系列之零拷贝技术
【08】Netty从0到1系列之整体架构、入门程序
【09】Netty从0到1系列之EventLoop
【10】Netty从0到1系列之EventLoopGroup
【11】Netty从0到1系列之Future
【12】Netty从0到1系列之Promise
【13】Netty从0到1系列之Netty Channel
【14】Netty从0到1系列之ChannelFuture
【15】Netty从0到1系列之CloseFuture


一、Netty Handler

网络处理的核心组件

1.1 Handler是什么?

Handler 是 Netty 框架中的核心处理组件,负责处理所有的 I/O 事件、业务逻辑和协议转换。它采用了责任链模式,允许开发者将多个 Handler 组织成一个处理流水线(Pipeline),每个 Handler 专注于完成特定的功能。

Handler 的核心特性:

  • 模块化设计:每个 Handler 只关注单一职责
  • 灵活组合:可以通过 Pipeline 自由组合多个 Handler
  • 双向处理:支持入站(Inbound)和出站(Outbound)处理
  • 线程安全:正确设计下可以保证线程安全
  • 生命周期管理:提供完整的生命周期回调
Handler 核心功能
处理入站数据
处理出站数据
管理连接生命周期
异常处理
数据转换与编码解码
业务逻辑处理

1.2 Handler的继承体系与类型

1.2.1 继承结构

ChannelHandler
    -> ChannelInboundHandler (处理入站事件和数据)
        -> ChannelInboundHandlerAdapter
        -> SimpleChannelInboundHandler
    
    -> ChannelOutboundHandler (处理出站事件和数据)
        -> ChannelOutboundHandlerAdapter
    
    -> ChannelDuplexHandler (同时处理入站和出站)

为了更好地理解 Handler 在 Netty 架构中的工作方式,请看下面的流程图:

ChannelPipeline
Inbound Handlers
Outbound Handlers
Outbound Handler 1
Outbound Handler 2
Outbound Handler 3
Inbound Handler 1
Inbound Handler 2
Inbound Handler 3
网络数据
应用程序
数据流入: 入站处理
数据流出: 出站处理

1.2.2 Handler体系全景图

ChannelHandler
ChannelInboundHandler
ChannelOutboundHandler
ChannelDuplexHandler
SimpleChannelInboundHandler
ChannelHandlerAdapter

🌟 核心思想

  • “事件驱动 + 责任链模式” —— 每个 Handler 是一个处理节点,形成 ChannelPipeline,事件在其中流动。

1.2.3 Handler分类

Netty 的 Handler 主要分为两大类,各自处理不同方向的事件:

ChannelHandler
ChannelInboundHandler
ChannelOutboundHandler
ChannelDuplexHandler
  • ChannelInboundHandler:处理入站事件,如接收数据、连接建立等
  • ChannelOutboundHandler:处理出站事件,如发送数据、关闭连接等
  • ChannelDuplexHandler:双向处理器,既可处理入站事件也可处理出站事件

1.2.4 责任链模式

Netty 的 Handler 采用责任链模式组织,形成一个 Pipeline(管道):

入站事件
InboundHandler1
InboundHandler2
InboundHandler3
出站事件
OutboundHandler3
OutboundHandler2
OutboundHandler1
  • 特点
    • 入站事件按 Handler 【添加顺序传播】
    • 出站事件按 Handler 【添加逆序传播】
    • 每个 Handler 可以决定是否将事件传递给下一个 Handler
    • 可以动态添加或移除 Handler

1.3 常用Handler接口与实现类

Netty 提供了丰富的 Handler 接口和实现类,满足不同场景需求:

ChannelHandler
ChannelInboundHandler
ChannelOutboundHandler
ChannelInboundHandlerAdapter
ChannelOutboundHandlerAdapter
ChannelDuplexHandler
SimpleChannelInboundHandler
ChannelInitializer

主要实现类说明:

  • ChannelInboundHandlerAdapter:入站 Handler 适配器,提供默认实现
  • ChannelOutboundHandlerAdapter:出站 Handler 适配器
  • SimpleChannelInboundHandler:简化的入站 Handler,自动释放消息
  • ChannelDuplexHandler:双向 Handler 基类
  • ChannelInitializer:特殊的 Handler,用于初始化新连接的 Pipeline

1.4 Handler核心接口

1.4.1 ChannelHandler

所有处理器的顶级接口,定义了生命周期方法:

public interface ChannelHandler {

    // 添加到 Pipeline 时调用
    void handlerAdded(ChannelHandlerContext ctx) throws Exception;

    // 从 Pipeline 移除时调用
    void handlerRemoved(ChannelHandlerContext ctx) throws Exception;

    // 标记该 Handler 可以被多个 Channel 共享
    @Inherited
    @Documented
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Sharable {
        // 标记后,同一个 Handler 实例可被多个 Channel 使用
    }
}

1.4.2 ChannelInboundHandler:处理入站事件

处理从网络流入的数据和事件:

public interface ChannelInboundHandler extends ChannelHandler {

    // Channel 激活(连接建立)
    void channelActive(ChannelHandlerContext ctx) throws Exception;

    // Channel 不活跃(连接断开)
    void channelInactive(ChannelHandlerContext ctx) throws Exception;

    // 接收到数据
    void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;

    // 数据读取完成
    void channelReadComplete(ChannelHandlerContext ctx) throws Exception;

    // 用户事件触发(如心跳)
    void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;

    // 异常发生
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}

1.4.3 ChannelOutboundHandler:处理出站事件

处理向外发送的数据和操作:

public interface ChannelOutboundHandler extends ChannelHandler {

    // 绑定本地地址
    void bind(ChannelHandlerContext ctx, SocketAddress localAddress,
              ChannelPromise promise) throws Exception;

    // 连接远程地址
    void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
                 SocketAddress localAddress, ChannelPromise promise) throws Exception;

    // 写数据
    void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;

    // 刷新缓冲区
    void flush(ChannelHandlerContext ctx) throws Exception;
}

1.4.4 ChannelDuplexHandler:全双工处理器

同时处理入站和出站事件,是 InboundOutbound 的组合。

1.4.5 Netty Handler层次结构示例

package cn.tcmeta.handler;

import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.util.CharsetUtil;

import java.util.concurrent.TimeUnit;

public class HandlerHierarchyExample {
    // 自定义入站Handler,继承自ChannelInboundHandlerAdapter
    static class CustomInboundHandler extends ChannelInboundHandlerAdapter {
        private final String name;

        public CustomInboundHandler(String name) {
            this.name = name;
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf in = (ByteBuf) msg;
            String message = in.toString(CharsetUtil.UTF_8);
            System.out.println(name + " 收到消息: " + message);

            // 将消息传递给下一个入站Handler
            ctx.fireChannelRead(msg);
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println(name + " 连接激活");
            super.channelActive(ctx);
        }
    }

    // 自定义出站Handler,继承自ChannelOutboundHandlerAdapter
    static class CustomOutboundHandler extends ChannelOutboundHandlerAdapter {
        private final String name;

        public CustomOutboundHandler(String name) {
            this.name = name;
        }

        @Override
        public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
            if (msg instanceof ByteBuf) {
                String message = ((ByteBuf) msg).toString(CharsetUtil.UTF_8);
                System.out.println(name + " 发送消息: " + message);
            }

            // 将消息传递给下一个出站Handler
            ctx.write(msg, promise);
        }
    }

    // 自定义双向Handler,继承自ChannelDuplexHandler
    static class CustomDuplexHandler extends ChannelDuplexHandler {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            System.out.println("双向Handler 收到消息");
            super.channelRead(ctx, msg);
        }

        @Override
        public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
            System.out.println("双向Handler 发送消息");
            super.write(ctx, msg, promise);
        }
    }

    // 自定义SimpleChannelInboundHandler
    static class CustomSimpleInboundHandler extends SimpleChannelInboundHandler<ByteBuf> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
            String message = msg.toString(CharsetUtil.UTF_8);
            System.out.println("SimpleInboundHandler 处理消息: " + message);

            // 发送响应
            String response = "已收到: " + message;
            ctx.writeAndFlush(Unpooled.copiedBuffer(response, CharsetUtil.UTF_8));
        }
    }

    public static void main(String[] args) throws Exception {
        // 启动服务器
        new Thread(() -> {
            try {
                startServer();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        // 等待服务器启动
        TimeUnit.SECONDS.sleep(1);

        // 启动客户端并发送消息
        startClient();
    }

    private static void startServer() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();

                            // 添加入站Handler(按添加顺序执行)
                            pipeline.addLast(new CustomInboundHandler("入站Handler 1"));
                            pipeline.addLast(new CustomInboundHandler("入站Handler 2"));

                            // 添加双向Handler
                            pipeline.addLast(new CustomDuplexHandler());

                            // 添加出站Handler(按添加逆序执行)
                            pipeline.addLast(new CustomOutboundHandler("出站Handler 1"));
                            pipeline.addLast(new CustomOutboundHandler("出站Handler 2"));

                            // 添加业务处理Handler
                            pipeline.addLast(new CustomSimpleInboundHandler());
                        }
                    });

            b.bind(8080).sync().channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private static void startClient() throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) {
                            // 客户端只添加一个简单的Handler用于发送消息
                            ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                                @Override
                                public void channelActive(ChannelHandlerContext ctx) {
                                    // 连接建立后发送消息
                                    ctx.writeAndFlush(Unpooled.copiedBuffer(
                                            "Hello, Netty Handler!", CharsetUtil.UTF_8));
                                }
                            });
                        }
                    });

            b.connect("localhost", 8080).sync().channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

入站Handler 1 连接激活
入站Handler 2 连接激活
入站Handler 1 收到消息: Hello, Netty Handler!
入站Handler 2 收到消息: Hello, Netty Handler!
双向Handler 收到消息
SimpleInboundHandler 处理消息: Hello, Netty Handler!
出站Handler 2 发送消息: 已收到: Hello, Netty Handler!
出站Handler 1 发送消息: 已收到: Hello, Netty Handler!
双向Handler 发送消息

1.5 常用抽象类

✅ ChannelInboundHandlerAdapter, [推荐使用】

  • ChannelInboundHandler 的适配器,空实现所有方法,你只需重写关心的方法。
    ✅ SimpleChannelInboundHandler<T>
  • 最常用的入站处理器,自动释放消息引用(ReferenceCountUtil.release(msg))。
public abstract class SimpleChannelInboundHandler<I> 
        extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        boolean release = true;
        try {
            if (acceptInboundMessage(msg)) {
                @SuppressWarnings("unchecked")
                I imsg = (I) msg;
                channelRead0(ctx, imsg); // 由子类实现
            } else {
                release = false;
                ctx.fireChannelRead(msg); // 传递给下一个 Handler
            }
        } finally {
            if (autoRelease && release) {
                ReferenceCountUtil.release(msg); // 自动释放
            }
        }
    }

    protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;
}

1.6 Handler使用示例

1.6.1 基础 Echo Server(回显服务器)

package cn.tcmeta.handler;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;

/**
 * @author: laoren
 * @description: Echo服务器
 * @version: 1.0.0
 */
@Slf4j
public class NettyEchoServer {
    private final int prot;

    public NettyEchoServer(int prot) {
        this.prot = prot;
    }

    public void doStart() {
        // 1. 创建 EventLoopGroup(线程组)
        EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 接受连接
        // 如果没有传入线程数量,则默认是cpu个数* 2
        EventLoopGroup workerGroup = new NioEventLoopGroup(2); // 处理 I/O

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new LoggingHandler())
                                    .addLast(new StringDecoder())
                                    .addLast(new EchoServerHandler());

                        }
                    }).childOption(ChannelOption.SO_KEEPALIVE, true);

            try {
                ChannelFuture future = bootstrap.bind(prot).sync();
                log.info("🚀 服务器启动成功,监听端口: {}", prot);
                future.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
            log.info("❌ 服务器已关闭 ~~~~");
        }
    }

    // 自定义Handler
    private static class EchoServerHandler extends SimpleChannelInboundHandler<String> {
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            log.info("🐦‍🔥 客户端连接: {}", ctx.channel().remoteAddress());
            ctx.writeAndFlush("欢迎连接到Echo Server ! \n");
        }

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            // 处理数据
            log.info("Received: {}", msg);
            // 将收到的消息重写发给客户端
            ctx.writeAndFlush(Unpooled.copiedBuffer("Echo: " + msg + "\n", StandardCharsets.UTF_8));
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            log.info("🔌 客户端断开: {}", ctx.channel().remoteAddress());
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            log.error("❌ 发生异常: {}", cause.getMessage());
            ctx.close();
        }
    }

    public static void main(String[] args) {
        new NettyEchoServer(8080).doStart();
    }
}

  • 客户端

在这里插入图片描述

  • 服务器日志
9:47:08 [DEBUG] [nioEventLoopGroup-3-2] i.n.h.l.LoggingHandler - [id: 0x23033458, L:/127.0.0.1:8080 - R:/127.0.0.1:2982] REGISTERED
19:47:08 [DEBUG] [nioEventLoopGroup-3-2] i.n.h.l.LoggingHandler - [id: 0x23033458, L:/127.0.0.1:8080 - R:/127.0.0.1:2982] ACTIVE
19:47:08 [INFO ] [nioEventLoopGroup-3-2] c.t.h.NettyEchoServer - 🐦‍🔥 客户端连接: /127.0.0.1:2982
19:47:08 [DEBUG] [nioEventLoopGroup-3-2] i.n.h.l.LoggingHandler - [id: 0x23033458, L:/127.0.0.1:8080 - R:/127.0.0.1:2982] WRITE: 欢迎连接到Echo Server ! 

19:47:08 [DEBUG] [nioEventLoopGroup-3-2] i.n.h.l.LoggingHandler - [id: 0x23033458, L:/127.0.0.1:8080 - R:/127.0.0.1:2982] FLUSH
19:47:13 [DEBUG] [nioEventLoopGroup-3-2] i.n.h.l.LoggingHandler - [id: 0x23033458, L:/127.0.0.1:8080 - R:/127.0.0.1:2982] READ: 12B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a             |hello world.    |
+--------+-------------------------------------------------+----------------+
19:47:13 [INFO ] [nioEventLoopGroup-3-2] c.t.h.NettyEchoServer - Received: hello world

19:47:13 [DEBUG] [nioEventLoopGroup-3-2] i.n.h.l.LoggingHandler - [id: 0x23033458, L:/127.0.0.1:8080 - R:/127.0.0.1:2982] WRITE: 19B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 45 63 68 6f 3a 20 68 65 6c 6c 6f 20 77 6f 72 6c |Echo: hello worl|
|00000010| 64 0a 0a                                        |d..             |
+--------+-------------------------------------------------+----------------+
19:47:13 [DEBUG] [nioEventLoopGroup-3-2] i.n.h.l.LoggingHandler - [id: 0x23033458, L:/127.0.0.1:8080 - R:/127.0.0.1:2982] FLUSH
19:47:13 [DEBUG] [nioEventLoopGroup-3-2] i.n.h.l.LoggingHandler - [id: 0x23033458, L:/127.0.0.1:8080 - R:/127.0.0.1:2982] READ COMPLETE

1.6.2 自定义编解码器Handler

  • 编码器
/**
 * @author: laoren
 * @description: 自定义编码器
 * @version: 1.0.0
 */
@Slf4j
public class MyStringEncoder extends ChannelOutboundHandlerAdapter {
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        if (msg instanceof ByteBuf) {
            log.info("MyStringEncoder --- encoder");
            ByteBuf buf = ctx.alloc().buffer();
            buf.writeBytes(((String) msg).getBytes(CharsetUtil.UTF_8));
            ctx.write(buf, promise);
        } else {
            // 不是String,直接传递
            ctx.write(msg, promise);
        }
    }
}
  • 解码器
/**
 * @author: laoren
 * @description: 解码器
 * @version: 1.0.0
 */
@Slf4j
public class MyStringDecoder extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof ByteBuf buf) {
            log.info("MyStringDecoder --- decoder");
            String str = buf.toString(io.netty.util.CharsetUtil.UTF_8);
            ctx.fireChannelRead(str);
        }else {
            ctx.fireChannelRead(msg); // 如果不是字符串,则直接传递
        }
    }
}
  • 使用
package cn.tcmeta.handler;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

/**
 * @author: laoren
 * @description: 使用自定义编码、解码器
 * @version: 1.0.0
 */
@Slf4j
public class CustomEchoServer {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workerGroup = new NioEventLoopGroup(2);

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new LoggingHandler())
                                    .addLast(new MyStringEncoder())
                                    .addLast(new MyStringDecoder());
                        }
                    }).childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture future = bootstrap.bind(8080).sync();
            log.info("Server started at port 8080");
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

服务端日志输出:

20:03:55 [INFO ] [main] c.t.h.CustomEchoServer - Server started at port 8080
20:04:13 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x582a339d, L:/127.0.0.1:8080 - R:/127.0.0.1:3315] REGISTERED
20:04:13 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x582a339d, L:/127.0.0.1:8080 - R:/127.0.0.1:3315] ACTIVE
20:04:22 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x582a339d, L:/127.0.0.1:8080 - R:/127.0.0.1:3315] READ: 6B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 68 65 6c 6c 6f 0a                               |hello.          |
+--------+-------------------------------------------------+----------------+
20:04:22 [INFO ] [nioEventLoopGroup-3-1] c.t.h.MyStringDecoder - MyStringDecoder --- decoder
20:04:22 [DEBUG] [nioEventLoopGroup-3-1] i.n.h.l.LoggingHandler - [id: 0x582a339d, L:/127.0.0.1:8080 - R:/127.0.0.1:3315] READ COMPLETE

1.6.3 出站Handler【记录日志】

/**
 * @author: laoren
 * @description: 日志记录出站Handler
 * @version: 1.0.0
 */
@Slf4j
public class LoggingOutboundHandler extends ChannelDuplexHandler {
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        log.info(" 📩 准备发送数据: {}", msg);
        super.write(ctx, msg, promise);
    }

    @Override
    public void flush(ChannelHandlerContext ctx) throws Exception {
        log.info(" 🆕 刷新缓冲区!");
        super.flush(ctx);
    }
}

1.6.4 使用 SimpleChannelInboundHandler

package cn.tcmeta.handler;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.ReferenceCountUtil;

// 泛型指定处理的消息类型,自动释放资源
public class TypedMessageHandler extends SimpleChannelInboundHandler<String> {

    private int messageCount = 0;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        // 只处理String类型的消息,自动释放资源
        messageCount++;
        System.out.println("收到第 " + messageCount + " 条消息: " + msg);

        // 业务处理逻辑
        String response = processMessage(msg);
        // 发送响应
        ctx.writeAndFlush(response);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("通道激活: " + ctx.channel().remoteAddress());
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("通道关闭,总共处理消息: " + messageCount);
        super.channelInactive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.err.println("消息处理异常: " + cause.getMessage());
        ctx.close();
    }

    private String processMessage(String message) {
        // 简单的业务逻辑处理
        if ("ping".equalsIgnoreCase(message)) {
            return "pong";
        } else if ("time".equalsIgnoreCase(message)) {
            return "当前时间: " + System.currentTimeMillis();
        } else {
            return "回声: " + message;
        }
    }
}

1.7 底层实现原理剖析

1.7.1 ✅ ChannelPipeline 与事件传播

Handler 通过 ChannelPipeline 组织成责任链,数据在链中传递:

// 简化的Pipeline实现原理
public class DefaultChannelPipeline implements ChannelPipeline {
    // 处理器链的头尾指针
    private final AbstractChannelHandlerContext head;
    private final AbstractChannelHandlerContext tail;
    
    // 添加处理器
    @Override
    public ChannelPipeline addLast(String name, ChannelHandler handler) {
        // 创建处理器上下文
        AbstractChannelHandlerContext newCtx = newContext(name, handler);
        
        // 添加到链尾
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
        
        return this;
    }
    
    // 事件传播
    private void invokeChannelRead(Object msg) {
        // 从头节点开始传播事件
        AbstractChannelHandlerContext ctx = head;
        while (ctx != null) {
            if (ctx.isInbound()) { // 只传播给入站处理器
                ctx.invokeChannelRead(msg);
            }
            ctx = ctx.next;
        }
    }
}

// 处理器上下文,维护处理器之间的关系
class AbstractChannelHandlerContext implements ChannelHandlerContext {
    volatile AbstractChannelHandlerContext next;
    volatile AbstractChannelHandlerContext prev;
    private final ChannelHandler handler;
    
    // 调用处理器的channelRead方法
    void invokeChannelRead(Object msg) {
        try {
            ((ChannelInboundHandler) handler).channelRead(this, msg);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    }
    
    // 将事件传播给下一个处理器
    @Override
    public ChannelHandlerContext fireChannelRead(Object msg) {
        AbstractChannelHandlerContext next = findContextInbound();
        next.invokeChannelRead(msg);
        return this;
    }
}
Outbound
Inbound
Tail Handler
Encoder
Business Handler
Decoder
Head Handler
网络数据到达
写入 Socket
  • Pipeline 是 Handler 的双向链表
  • 入站事件:fireChannelRead() 从 Head 向 Tail 传播
  • 出站事件:write() 从 Tail向 Head 传播

1.7.2 Handler线程模型

Handler 的执行与 EventLoop 紧密相关,遵循重要的线程规则:

public class ThreadAwareHandler extends ChannelInboundHandlerAdapter {
    private volatile Thread handlerThread;
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 记录处理线程
        if (handlerThread == null) {
            handlerThread = Thread.currentThread();
        }
        
        // 检查是否在正确的线程中执行
        if (ctx.executor().inEventLoop()) {
            System.out.println("在EventLoop线程中执行: " + Thread.currentThread().getName());
            processMessage(msg);
        } else {
            System.err.println("警告:在非EventLoop线程中执行!");
            // 应该提交到正确的EventLoop中执行
            ctx.executor().execute(() -> processMessage(msg));
        }
    }
    
    private void processMessage(Object msg) {
        // 模拟消息处理
        try {
            Thread.sleep(100); // 模拟耗时操作
            System.out.println("处理消息: " + msg);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Handler被添加到Pipeline");
    }
    
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Handler从Pipeline中移除");
    }
}

1.7.2 ✅ ChannelHandlerContext

  • 每个 HandlerPipeline 中都有一个对应的 Context
  • 用于事件传播资源访问
  • 调用 ctx.fireChannelRead() 表示传递给下一个 Handler
  • 调用 ctx.write() 表示从当前节点开始向前传播

1.7.3 ✅ 内存管理与引用计数

  • Netty 使用 ByteBuf 实现零拷贝
  • SimpleChannelInboundHandler自动释放 msg
  • 手动处理时需调用 ReferenceCountUtil.release(msg)

1.8 实践经验与最佳实践

1.8.1 Handler 设计与使用最佳实践

package cn.tcmeta.handler;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

import java.util.concurrent.atomic.AtomicLong;

// 使用@Sharable注解标记可共享的Handler(必须是线程安全的)
@ChannelHandler.Sharable
public class BestPracticeHandler extends ChannelInboundHandlerAdapter {

    // 使用原子变量保证线程安全
    private final AtomicLong messageCounter = new AtomicLong(0);
    private final AtomicLong totalBytes = new AtomicLong(0);

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            long count = messageCounter.incrementAndGet();

            if (msg instanceof String) {
                String message = (String) msg;
                totalBytes.addAndGet(message.getBytes().length);
                // 业务处理逻辑
                String result = processBusinessLogic(message);
                // 发送响应
                ctx.writeAndFlush(result);

                // 每100条消息打印一次统计信息
                if (count % 100 == 0) {
                    System.out.println("已处理 " + count + " 条消息,总字节数: " + totalBytes.get());
                }
            } else {
                // 无法处理的消息类型,传递给下一个Handler
                ctx.fireChannelRead(msg);
            }
        } finally {
            // 确保释放资源
            ReferenceCountUtil.release(msg);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 分类处理不同类型的异常
        if (cause instanceof java.io.IOException) {
            System.err.println("网络IO异常: " + cause.getMessage());
            // 网络异常通常直接关闭连接
            ctx.close();
        } else if (cause instanceof IllegalArgumentException) {
            System.err.println("协议解析异常: " + cause.getMessage());
            // 协议异常可以发送错误响应
            ctx.writeAndFlush("ERROR: " + cause.getMessage());
        } else {
            System.err.println("未知异常: " + cause.getMessage());
            // 其他异常记录日志并关闭连接
            ctx.close();
        }
    }

    // 业务逻辑处理方法
    private String processBusinessLogic(String message) {
        // 模拟业务处理
        if (message == null || message.isEmpty()) {
            throw new IllegalArgumentException("消息不能为空");
        }

        // 简单的回声服务
        return "ECHO: " + message;
    }

    // 获取统计信息
    public long getMessageCount() {
        return messageCounter.get();
    }

    public long getTotalBytes() {
        return totalBytes.get();
    }

    // 重置统计信息
    public void resetStatistics() {
        messageCounter.set(0);
        totalBytes.set(0);
    }
}

// 高性能的Handler,避免阻塞操作
class HighPerformanceHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 快速处理或提交到业务线程池
        if (canProcessFast(msg)) {
            processFast(msg);
        } else {
            // 耗时操作提交到业务线程池
            BusinessExecutor.submit(() -> {
                try {
                    Object result = processSlowly(msg);
                    // 将结果写回EventLoop线程
                    ctx.executor().execute(() -> {
                        ctx.writeAndFlush(result);
                    });
                } catch (Exception e) {
                    ctx.executor().execute(() -> {
                        ctx.fireExceptionCaught(e);
                    });
                }
            });
        }
    }

    private boolean canProcessFast(Object msg) {
        // 判断是否能够快速处理
        return msg instanceof String && ((String) msg).length() < 1024;
    }

    private void processFast(Object msg) {
        // 快速处理逻辑
    }

    private Object processSlowly(Object msg) {
        // 耗时处理逻辑
        try {
            Thread.sleep(1000); // 模拟耗时操作
            return "Processed: " + msg;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
    }
}

// 业务线程池
class BusinessExecutor {
    private static final java.util.concurrent.ExecutorService executor =
            java.util.concurrent.Executors.newFixedThreadPool(10);

    public static void submit(Runnable task) {
        executor.submit(task);
    }
}

1.8.2 Handler生命周期管理

package cn.tcmeta.handler;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.AttributeKey;

public class LifecycleAwareHandler extends ChannelInboundHandlerAdapter {
    
    // 使用AttributeKey在Channel上存储状态
    private static final AttributeKey<SessionState> SESSION_KEY = 
        AttributeKey.valueOf("sessionState");
    
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Handler被添加到Pipeline: " + ctx.name());
        // 初始化会话状态
        ctx.channel().attr(SESSION_KEY).set(new SessionState());
    }
    
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Handler从Pipeline移除: " + ctx.name());
        // 清理资源
        SessionState state = ctx.channel().attr(SESSION_KEY).getAndSet(null);
        if (state != null) {
            state.cleanup();
        }
    }
    
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Channel注册到EventLoop");
        super.channelRegistered(ctx);
    }
    
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Channel从EventLoop注销");
        super.channelUnregistered(ctx);
    }
    
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Channel激活: " + ctx.channel().remoteAddress());
        // 会话开始
        SessionState state = ctx.channel().attr(SESSION_KEY).get();
        state.setStartTime(System.currentTimeMillis());
        super.channelActive(ctx);
    }
    
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Channel关闭");
        // 会话结束
        SessionState state = ctx.channel().attr(SESSION_KEY).get();
        if (state != null) {
            long duration = System.currentTimeMillis() - state.getStartTime();
            System.out.println("会话持续时间: " + duration + "ms");
        }
        super.channelInactive(ctx);
    }
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 更新会话状态
        SessionState state = ctx.channel().attr(SESSION_KEY).get();
        state.incrementMessageCount();
        
        System.out.println("收到第 " + state.getMessageCount() + " 条消息");
        ctx.fireChannelRead(msg);
    }
    
    // 会话状态类
    private static class SessionState {
        private long startTime;
        private long messageCount;
        
        public long getStartTime() {
            return startTime;
        }
        
        public void setStartTime(long startTime) {
            this.startTime = startTime;
        }
        
        public long getMessageCount() {
            return messageCount;
        }
        
        public void incrementMessageCount() {
            messageCount++;
        }
        
        public void cleanup() {
            // 清理资源
            System.out.println("清理会话资源,总消息数: " + messageCount);
        }
    }
}

1.9 最佳实践与常见陷阱

1.9.1 ✅ 推荐实践

实践说明
优先使用 SimpleChannelInboundHandler自动释放资源,避免内存泄漏
使用 @Sharable 标记无状态 Handler提高性能,减少对象创建
exceptionCaught 中关闭连接防止资源泄漏
避免在 Handler 中阻塞Thread.sleep()
合理组织 Pipeline解码 -> 业务 -> 编码

1.9.2 ⚠️ 常见错误

// ❌ 错误:忘记释放 ByteBuf
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ByteBuf buf = (ByteBuf) msg;
    // ... 处理
    // ❌ 忘记 release
}

// ✅ 正确:使用 SimpleChannelInboundHandler 或手动释放
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
    // 自动释放
}

1.10 Handler优缺点总结

1.10.1 ✅ 优点

优点说明
高度解耦业务逻辑与网络层分离
可扩展性强通过 Pipeline 灵活组合
支持编解码内置丰富编解码器
线程安全事件在 EventLoop 线程串行执行
内存高效支持零拷贝和引用计数

1.10.2 ❌ 缺点

缺点说明
学习曲线陡峭需理解 Pipeline 和事件传播
调试复杂事件在多个 Handler 间流动
内存泄漏风险忘记释放 ByteBuf
过度设计风险简单场景可能过于复杂

1.11 总结: Handler的核心价值

维度说明
核心角色Netty 业务逻辑的“处理器”
设计模式责任链模式 + 事件驱动
核心能力处理入站/出站事件、编解码、异常处理
适用场景所有网络通信场景:RPC、HTTP、WebSocket、MQTT 等
Netty 地位EventLoopFuture 并列为三大核心组件

1.12 一句话总结

Netty 的 Handler 是网络编程的“大脑”

  • 它通过 Pipeline 将复杂的网络操作分解为可复用、可组合的处理单元
  • 构建高性能、高可维护性网络应用核心架构模式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值