Netty中重要的类

1 ChannelFuture

io.netty.channel.ChannelFuture 是 Netty 框架中一个非常重要的接口,它代表了一个异步 Channel I/O 操作的结果。由于 Netty 中的所有 I/O 操作都是异步的,因此任何 I/O 调用都会立即返回,而不会保证在调用结束时请求的 I/O 操作已经完成。相反,你会得到一个 ChannelFuture 实例,它可以让你了解 I/O 操作的结果或状态。以下是对 ChannelFuture 类的详细解析:

接口定义
public interface ChannelFuture extends Future<Void> {
    // ...
}

ChannelFuture 接口继承自 io.netty.util.concurrent.Future<Void>,这意味着它具备了通用异步操作结果的基本特性,同时专门针对 Channel 的 I/O 操作进行了扩展。

状态模型

ChannelFuture 有两种主要状态:未完成(uncompleted)和已完成(completed)。

  • 未完成:当一个 I/O 操作开始时,会创建一个新的 ChannelFuture 对象,此时它处于未完成状态。这意味着 I/O 操作尚未结束,它既没有成功、失败,也没有被取消。
  • 已完成:当 I/O 操作成功完成、失败或被取消时,ChannelFuture 会被标记为已完成,并包含更具体的信息,如失败的原因。

状态转换图如下:

                                      +---------------------------+
                                      | Completed successfully    |
                                      +---------------------------+
                                 +---->      isDone() = true      |
+--------------------------+    |    |   isSuccess() = true      |
|        Uncompleted       |    |    +===========================+
+--------------------------+    |    | Completed with failure    |
|      isDone() = false    |    |    +---------------------------+
|   isSuccess() = false    |----+---->      isDone() = true      |
| isCancelled() = false    |    |    |       cause() = non-null  |
|       cause() = null     |    |    +===========================+
+--------------------------+    |    | Completed by cancellation |
                                 |    +---------------------------+
                                 +---->      isDone() = true      |
                                      | isCancelled() = true      |
                                      +---------------------------+
核心方法
1. Channel channel()

返回与该 ChannelFuture 关联的 Channel 对象,即 I/O 操作发生的通道。

Channel channel = future.channel();
2. 监听相关方法

这些方法用于添加和移除 GenericFutureListener,当 ChannelFuture 完成时,监听器会被通知。

@Override
ChannelFuture addListener(GenericFutureListener<? extends Future<? super Void>> listener);
@Override
ChannelFuture addListeners(GenericFutureListener<? extends Future<? super Void>>... listeners);
@Override
ChannelFuture removeListener(GenericFutureListener<? extends Future<? super Void>> listener);
@Override
ChannelFuture removeListeners(GenericFutureListener<? extends Future<? super Void>>... listeners);

示例:

future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
        if (future.isSuccess()) {
            // 操作成功处理逻辑
        } else {
            // 操作失败处理逻辑
            Throwable cause = future.cause();
            cause.printStackTrace();
        }
    }
});
3. 同步相关方法

这些方法用于等待 ChannelFuture 完成,并处理可能的异常。

@Override
ChannelFuture sync() throws InterruptedException;
@Override
ChannelFuture syncUninterruptibly();
  • sync():等待 ChannelFuture 完成,如果操作失败则抛出异常,该方法会响应线程中断。
  • syncUninterruptibly():与 sync() 类似,但不会响应线程中断。

示例:

try {
    future.sync();
    // 操作成功处理逻辑
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    // 线程被中断处理逻辑
} catch (Exception e) {
    // 操作失败处理逻辑
}
4. 等待相关方法

这些方法用于等待 ChannelFuture 完成,可以指定超时时间。

@Override
ChannelFuture await() throws InterruptedException;
@Override
ChannelFuture awaitUninterruptibly();
boolean await(long timeout, TimeUnit unit) throws InterruptedException;
boolean await(long timeoutMillis) throws InterruptedException;
boolean awaitUninterruptibly(long timeout, TimeUnit unit);
boolean awaitUninterruptibly(long timeoutMillis);
  • await():等待 ChannelFuture 完成,该方法会响应线程中断。
  • awaitUninterruptibly():与 await() 类似,但不会响应线程中断。
  • await(long timeout, TimeUnit unit)await(long timeoutMillis):等待 ChannelFuture 在指定时间内完成,返回操作是否在超时时间内完成,该方法会响应线程中断。
  • awaitUninterruptibly(long timeout, TimeUnit unit)awaitUninterruptibly(long timeoutMillis):与上述方法类似,但不会响应线程中断。

示例:

try {
    if (future.await(5, TimeUnit.SECONDS)) {
        if (future.isSuccess()) {
            // 操作成功处理逻辑
        } else {
            // 操作失败处理逻辑
            Throwable cause = future.cause();
            cause.printStackTrace();
        }
    } else {
        // 操作超时处理逻辑
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    // 线程被中断处理逻辑
}
5. boolean isVoid()

返回该 ChannelFuture 是否为无效的未来,如果是,则不允许调用一些特定的方法,如 addListenerawait 等。

最佳实践建议
1. 优先使用 addListener 而非 await

addListener 是非阻塞的,它只是将指定的监听器添加到 ChannelFuture 中,当与该未来关联的 I/O 操作完成时,I/O 线程会通知监听器。这种方式性能和资源利用率最佳,因为它不会阻塞任何线程。而 await 是阻塞操作,调用者线程会一直阻塞直到操作完成,可能会导致不必要的线程阻塞和较高的线程间通知成本,甚至在特定情况下可能会导致死锁。

示例:

// 推荐方式
future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
        if (future.isSuccess()) {
            // 操作成功处理逻辑
        } else {
            // 操作失败处理逻辑
            Throwable cause = future.cause();
            cause.printStackTrace();
        }
    }
});

// 不推荐方式
try {
    future.await();
    if (future.isSuccess()) {
        // 操作成功处理逻辑
    } else {
        // 操作失败处理逻辑
        Throwable cause = future.cause();
        cause.printStackTrace();
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    // 线程被中断处理逻辑
}
2. 避免在 ChannelHandler 中调用 await

ChannelHandler 中的事件处理方法通常由 I/O 线程调用,如果在这些方法中调用 await,可能会导致死锁,因为 await 会阻塞 I/O 操作。

示例:

// 错误示例
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ChannelFuture future = ctx.channel().close();
    future.awaitUninterruptibly();
    // 执行关闭后的操作
}

// 正确示例
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ChannelFuture future = ctx.channel().close();
    future.addListener(new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            // 执行关闭后的操作
        }
    });
}
实现类

Netty 提供了多个 ChannelFuture 的实现类,如 CompleteChannelFutureFailedChannelFutureSucceededChannelFuture 等,用于表示不同状态的 ChannelFuture。这些实现类在内部维护了 ChannelFuture 的状态和结果,并实现了接口定义的各种方法。

2 io.netty.channel.EventLoop

它主要负责处理 Channel 的所有 I/O 操作。下面将从接口定义、主要功能、核心方法、与相关类的关系以及使用示例几个方面对 EventLoop 进行详细解析。

接口定义
public interface EventLoop extends OrderedEventExecutor, EventLoopGroup {
    @Override
    EventLoopGroup parent();
}

从接口定义可以看出,EventLoop 继承自 OrderedEventExecutorEventLoopGroupOrderedEventExecutor 确保任务按顺序执行,而 EventLoopGroup 表示一组 EventLoopparent() 方法用于返回该 EventLoop 所属的 EventLoopGroup

主要功能
  • I/O 操作处理EventLoop 的核心功能是处理 Channel 的 I/O 操作,包括连接建立、数据读写、连接关闭等。它负责监控 Channel 的状态变化,并在有 I/O 事件发生时执行相应的处理逻辑。
  • 任务调度:除了处理 I/O 操作,EventLoop 还可以调度和执行普通的任务。这些任务可以是用户自定义的定时任务或异步任务。
  • 线程管理:每个 EventLoop 通常与一个线程关联,确保 Channel 的操作在同一个线程中执行,避免了多线程并发带来的复杂性和同步问题。
核心方法

虽然 EventLoop 接口中只定义了 parent() 方法,但它继承了 OrderedEventExecutorEventLoopGroup 的许多重要方法:

继承自 OrderedEventExecutor 的方法
  • execute(Runnable task):将一个任务提交到 EventLoop 中执行。该任务会在 EventLoop 关联的线程中被调度执行。
eventLoop.execute(() -> {
    // 执行具体的任务逻辑
    System.out.println("Task is being executed in the EventLoop.");
});
  • submit(Callable<T> task):提交一个带有返回值的任务到 EventLoop 中执行,并返回一个 Future 对象,用于获取任务的执行结果。
Future<Integer> future = eventLoop.submit(() -> {
    // 执行具体的任务逻辑并返回结果
    return 42;
});
继承自 EventLoopGroup 的方法
  • next():返回 EventLoopGroup 中的下一个 EventLoop。通常用于在多个 EventLoop 之间进行负载均衡。
EventLoop nextEventLoop = eventLoopGroup.next();
  • register(Channel channel):将一个 Channel 注册到 EventLoop 中,以便 EventLoop 可以处理该 Channel 的 I/O 操作。
ChannelFuture registerFuture = eventLoop.register(channel);
与相关类的关系
  • EventLoopGroupEventLoopEventLoopGroup 的成员,一个 EventLoopGroup 可以包含多个 EventLoopEventLoopGroup 负责管理和分配 EventLoop,并提供了统一的接口来操作这些 EventLoop
  • ChannelEventLoopChannel 紧密相关,每个 Channel 都需要注册到一个 EventLoop 中,以便 EventLoop 可以处理该 Channel 的 I/O 操作。一个 EventLoop 可以同时处理多个 Channel 的 I/O 操作。
使用示例

以下是一个简单的使用 EventLoop 的示例,展示了如何创建 EventLoopGroup、获取 EventLoop 并提交任务:

import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;

import java.util.concurrent.TimeUnit;

public class EventLoopExample {
    public static void main(String[] args) {
        // 创建一个 NioEventLoopGroup,包含 2 个 EventLoop
        EventLoopGroup group = new NioEventLoopGroup(2);

        try {
            // 获取一个 EventLoop
            EventLoop eventLoop = group.next();

            // 提交一个任务到 EventLoop 中执行
            eventLoop.execute(() -> {
                System.out.println("Task is being executed in the EventLoop.");
            });

            // 提交一个定时任务到 EventLoop 中执行
            eventLoop.schedule(() -> {
                System.out.println("Scheduled task is being executed after 2 seconds.");
            }, 2, TimeUnit.SECONDS);

            // 保持主线程存活一段时间,以便观察任务的执行结果
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 优雅地关闭 EventLoopGroup
            group.shutdownGracefully();
        }
    }
}

在这个示例中,我们首先创建了一个 NioEventLoopGroup,它包含 2 个 EventLoop。然后,我们通过 next() 方法获取一个 EventLoop,并向该 EventLoop 提交了一个普通任务和一个定时任务。最后,我们优雅地关闭了 EventLoopGroup

综上所述,EventLoop 是 Netty 异步 I/O 模型的核心组件之一,它负责处理 Channel 的 I/O 操作和任务调度,为 Netty 应用程序提供了高效、可靠的并发处理能力。

3 io.netty.channel.EventLoopGroup

它在管理和调度 EventLoop 实例时发挥着核心作用,这些 EventLoop 实例负责处理 Channel 的所有 I/O 操作。下面我们将从多个方面对 EventLoopGroup 进行详细解析。

接口定义
public interface EventLoopGroup extends EventExecutorGroup {
    /**
     * Return the next {@link EventLoop} to use
     */
    @Override
    EventLoop next();

    /**
     * Register a {@link Channel} with this {@link EventLoop}. The returned {@link ChannelFuture}
     * will get notified once the registration was complete.
     */
    ChannelFuture register(Channel channel);

    /**
     * Register a {@link Channel} with this {@link EventLoop} using a {@link ChannelFuture}. The passed
     * {@link ChannelFuture} will get notified once the registration was complete and also will get returned.
     */
    ChannelFuture register(ChannelPromise promise);

    /**
     * Register a {@link Channel} with this {@link EventLoop}. The passed {@link ChannelFuture}
     * will get notified once the registration was complete and also will get returned.
     *
     * @deprecated Use {@link #register(ChannelPromise)} instead.
     */
    @Deprecated
    ChannelFuture register(Channel channel, ChannelPromise promise);
}
主要功能
  1. 继承自 EventExecutorGroupEventLoopGroup 继承自 EventExecutorGroup,这意味着它拥有 EventExecutorGroup 的所有功能,如任务执行、任务调度、生命周期管理等。
  2. 获取下一个 EventLoopnext() 方法用于返回下一个可用的 EventLoop 实例。在多线程环境下,EventLoopGroup 会负责管理多个 EventLoop 实例,并根据一定的策略(如轮询)选择一个 EventLoop 来处理新的 Channel
  3. Channel 注册register() 方法用于将 Channel 注册到 EventLoop 中。注册完成后,Channel 的所有 I/O 操作都将由该 EventLoop 负责处理。注册操作是异步的,返回的 ChannelFuture 可以用于监听注册操作的完成状态。
主要实现类
  1. MultithreadEventLoopGroup

    这是一个抽象类,它是多个EventLoop实现的基础。它使用多个线程来处理Channel的 I/O 操作,默认线程数通常为 CPU 核心数的两倍。

public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {
// …
}


2. `NioEventLoopGroup`

:基于 Java NIO(Non-blocking I/O)实现的EventLoopGroup,用于处理基于Selector的Channel。它使用多个线程来处理Channel的 I/O 操作,适用于大多数网络编程场景

```java
@Deprecated
public class NioEventLoopGroup extends MultiThreadIoEventLoopGroup implements IoEventLoopGroup {
    // ...
}
  1. EpollEventLoopGroup

    基于 Linux 的epoll机制实现的EventLoopGroup,只能在 Linux 系统上使用。它提供了比 NIO 更高的性能,特别适用于高并发的网络编程场景。

    @Deprecated
    public final class EpollEventLoopGroup extends MultiThreadIoEventLoopGroup {
        // ...
    }
    
  2. OioEventLoopGroup

    :基于 Java OIO(Blocking I/O)实现的EventLoopGroup,每个Channel

    由一个独立的线程处理。由于 OIO 是阻塞式 I/O,这种实现方式在高并发场景下性能较差,已被弃用。

    @Deprecated
    public class OioEventLoopGroup extends ThreadPerChannelEventLoopGroup {
        // ...
    }
    
使用示例

以下是一个简单的 Netty 服务器示例,展示了如何使用 NioEventLoopGroup

public class NettyServer {
    private final int port;

    public NettyServer(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        // 创建两个 EventLoopGroup 实例
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // 用于处理客户端连接
        EventLoopGroup workerGroup = new NioEventLoopGroup(); // 用于处理网络读写
        try {
            // 创建 ServerBootstrap 实例
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
              .channel(NioServerSocketChannel.class)
              .childHandler(new ChannelInitializer<SocketChannel>() {
                  @Override
                  public void initChannel(SocketChannel ch) throws Exception {
                      // 添加 ChannelHandler 到 ChannelPipeline
                      // ch.pipeline().addLast(new YourServerHandler());
                  }
              })
              .option(ChannelOption.SO_BACKLOG, 128)
              .childOption(ChannelOption.SO_KEEPALIVE, true);

            // 绑定端口并开始接收连接
            ChannelFuture f = b.bind(port).sync();

            // 等待服务器关闭
            f.channel().closeFuture().sync();
        } finally {
            // 优雅地关闭 EventLoopGroup
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        new NettyServer(port).run();
    }
}

4 io.netty.channel.ChannelOutboundInvoker

io.netty.channel.ChannelOutboundInvoker 是 Netty 框架中一个关键的接口,操作,这些操作通常涉及将数据从应用程序发送到网络。以下是对该接口的详细解析:

接口概述

ChannelOutboundInvoker 接口定义了一系列方法,用于发起各种出站操作,如绑定地址、连接远程服务器、断开连接、关闭通道等。这些操作会在 ChannelPipeline 中触发相应的 ChannelOutboundHandler 处理。

主要方法及功能
1. 绑定操作
default ChannelFuture bind(SocketAddress localAddress) {
    return bind(localAddress, newPromise());
}

ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise);
  • 功能:请求将 Channel 绑定到指定的本地地址。bind 方法会触发 ChannelPipeline 中下一个 ChannelOutboundHandlerbind 方法。
  • 参数:
    • localAddress:要绑定的本地地址。
    • promise:用于通知操作完成的 ChannelPromise 对象。
  • 返回值:一个 ChannelFuture 对象,用于监听绑定操作的完成状态。
2. 连接操作
default ChannelFuture connect(SocketAddress remoteAddress) {
    return connect(remoteAddress, newPromise());
}

default ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
    return connect(remoteAddress, localAddress, newPromise());
}

ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise);

ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
  • 功能:请求将 Channel 连接到指定的远程地址。如果指定了本地地址,则在连接时绑定到该本地地址。连接操作会触发 ChannelPipeline 中下一个 ChannelOutboundHandlerconnect 方法。
  • 参数:
    • remoteAddress:要连接的远程地址。
    • localAddress:可选的本地地址。
    • promise:用于通知操作完成的 ChannelPromise 对象。
  • 返回值:一个 ChannelFuture 对象,用于监听连接操作的完成状态。
3. 断开连接操作
default ChannelFuture disconnect() {
    return disconnect(newPromise());
}

ChannelFuture disconnect(ChannelPromise promise);
  • 功能:请求断开 Channel 与远程对等方的连接。断开连接操作会触发 ChannelPipeline 中下一个 ChannelOutboundHandlerdisconnect 方法。
  • 参数:
    • promise:用于通知操作完成的 ChannelPromise 对象。
  • 返回值:一个 ChannelFuture 对象,用于监听断开连接操作的完成状态。
4. 关闭操作
default ChannelFuture close() {
    return close(newPromise());
}

ChannelFuture close(ChannelPromise promise);
  • 功能:请求关闭 Channel。关闭后,Channel 不能再被重用。关闭操作会触发 ChannelPipeline 中下一个 ChannelOutboundHandlerclose 方法。
  • 参数:
    • promise:用于通知操作完成的 ChannelPromise 对象。
  • 返回值:一个 ChannelFuture 对象,用于监听关闭操作的完成状态。
5. 取消注册操作
default ChannelFuture deregister() {
    return deregister(newPromise());
}

ChannelFuture deregister(ChannelPromise promise);
  • 功能:请求将 Channel 从之前分配的 EventExecutor 中取消注册。取消注册操作会触发 ChannelPipeline 中下一个 ChannelOutboundHandlerderegister 方法。
  • 参数:
    • promise:用于通知操作完成的 ChannelPromise 对象。
  • 返回值:一个 ChannelFuture 对象,用于监听取消注册操作的完成状态。
6. 读取操作
ChannelOutboundInvoker read();
  • 功能:请求从 Channel 读取数据到第一个入站缓冲区。如果读取到数据,会触发 ChannelInboundHandlerchannelRead 事件;读取完成后,会触发 channelReadComplete 事件。读取操作会触发 ChannelPipeline 中下一个 ChannelOutboundHandlerread 方法。
  • 返回值:返回 ChannelOutboundInvoker 本身,以便进行链式调用。
使用示例

以下是一个简单的示例,展示了如何使用 ChannelOutboundInvoker 进行连接和关闭操作:

public class ChannelOutboundInvokerExample {
    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                     .channel(NioSocketChannel.class)
                     .handler(new ChannelInitializer<SocketChannel>() {
                         @Override
                         protected void initChannel(SocketChannel ch) throws Exception {
                             // 添加 ChannelHandler
                         }
                     });

            // 连接到远程服务器
            ChannelFuture connectFuture = bootstrap.connect(new InetSocketAddress("example.com", 80));
            connectFuture.sync();

            Channel channel = connectFuture.channel();

            // 关闭 Channel
            ChannelFuture closeFuture = channel.close();
            closeFuture.sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

5 io.netty.channel.ChannelPromise

它继承自 ChannelFuturePromise<Void>,代表一个可写的 ChannelFuture,主要用于管理和跟踪 Channel 操作的异步结果。以下是对其详细解析:

作用概述

ChannelPromise 的核心作用是提供一种机制,让开发者可以异步地处理 Channel 相关操作的结果。在 Netty 中,许多 Channel 操作(如绑定、连接、读写等)都是异步执行的,这意味着调用这些操作的方法会立即返回,而实际的操作结果会在未来的某个时间点完成。ChannelPromise 允许开发者注册监听器,在操作完成时得到通知,还可以阻塞当前线程直到操作完成。

主要方法及功能
1. 状态设置方法
ChannelPromise setSuccess(Void result);
ChannelPromise setSuccess();
boolean trySuccess();
ChannelPromise setFailure(Throwable cause);
  • 功能:这些方法用于设置 ChannelPromise 的状态。setSuccess 方法将 Promise 的状态设置为成功,setFailure 方法将其设置为失败。trySuccess 方法尝试将 Promise 的状态设置为成功,如果已经被设置过,则返回 false
  • 示例
ChannelPromise promise = channel.newPromise();
if (someCondition) {
    promise.setSuccess();
} else {
    promise.setFailure(new RuntimeException("Operation failed"));
}
2. 监听器管理方法
ChannelPromise addListener(GenericFutureListener<? extends Future<? super Void>> listener);
ChannelPromise addListeners(GenericFutureListener<? extends Future<? super Void>>... listeners);
ChannelPromise removeListener(GenericFutureListener<? extends Future<? super Void>> listener);
ChannelPromise removeListeners(GenericFutureListener<? extends Future<? super Void>>... listeners);
  • 功能:这些方法用于添加和移除 ChannelPromise 的监听器。当 Promise 的状态变为成功或失败时,所有注册的监听器都会被通知。
  • 示例
ChannelPromise promise = channel.newPromise();
promise.addListener(future -> {
    if (future.isSuccess()) {
        System.out.println("Operation succeeded");
    } else {
        System.err.println("Operation failed: " + future.cause());
    }
});
3. 同步方法
ChannelPromise sync() throws InterruptedException;
ChannelPromise syncUninterruptibly();
ChannelPromise await() throws InterruptedException;
ChannelPromise awaitUninterruptibly();
  • 功能:这些方法用于阻塞当前线程,直到 ChannelPromise 的状态变为成功或失败。syncawait 方法会抛出 InterruptedException,如果线程在等待过程中被中断;syncUninterruptiblyawaitUninterruptibly 方法则不会抛出该异常。
  • 示例
ChannelPromise promise = channel.newPromise();
try {
    promise.sync();
    System.out.println("Operation completed");
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}
4. 其他方法
Channel channel();
ChannelPromise unvoid();
  • 功能channel 方法返回与该 ChannelPromise 关联的 Channelunvoid 方法用于将一个 void 类型的 ChannelPromise 转换为普通的 ChannelPromise

#### 主要实现类

1. DefaultChannelPromise
public class DefaultChannelPromise extends DefaultPromise<Void> implements ChannelPromise, FlushCheckpoint {
    // ...
}
  • 功能DefaultChannelPromiseChannelPromise 的默认实现类。它继承自 DefaultPromise<Void>,并实现了 FlushCheckpoint 接口。通常建议使用 Channel.newPromise() 方法来创建 DefaultChannelPromise 实例。
2. VoidChannelPromise
@UnstableApi
public final class VoidChannelPromise extends AbstractFuture<Void> implements ChannelPromise {
    // ...
}
  • 功能VoidChannelPromise 是一个特殊的 ChannelPromise 实现,它表示一个不可用的 Promise。当调用其方法时,通常会抛出 IllegalStateException 异常。
使用示例

以下是一个简单的示例,展示了如何使用 ChannelPromise 来管理 Channel 的连接操作:

public class ChannelPromiseExample {
    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                     .channel(NioSocketChannel.class)
                     .option(ChannelOption.TCP_NODELAY, true)
                     .handler(new ChannelInitializer<SocketChannel>() {
                         @Override
                         public void initChannel(SocketChannel ch) throws Exception {
                             ch.pipeline().addLast(new SimpleChannelInboundHandler<Object>() {
                                 @Override
                                 protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
                                     // 处理接收到的数据
                                 }
                             });
                         }
                     });

            // 创建一个 ChannelPromise
            ChannelPromise promise = bootstrap.config().group().next().newPromise();

            // 发起连接操作
            ChannelFuture connectFuture = bootstrap.connect(new InetSocketAddress("example.com", 80));
            connectFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (future.isSuccess()) {
                        promise.setSuccess();
                    } else {
                        promise.setFailure(future.cause());
                    }
                }
            });

            // 等待连接操作完成
            promise.sync();

            if (promise.isSuccess()) {
                Channel channel = connectFuture.channel();
                // 连接成功,进行后续操作
            } else {
                System.err.println("Connection failed: " + promise.cause());
            }
        } finally {
            group.shutdownGracefully();
        }
    }
}

6 io.netty.channel.Channel

它代表了一个与网络套接字或能够执行 I/O 操作(如读、写、连接和绑定)的组件的连接点。以下是对 Channel 接口的详细解析:

接口概述

Channel 接口为用户提供了对通道的各种操作和状态的访问,包括通道的当前状态(如是否打开、是否连接)、通道的配置参数、通道支持的 I/O 操作以及处理与通道相关的所有 I/O 事件和请求的 ChannelPipeline

主要功能
1. 提供通道状态信息
  • ChannelId id():返回该通道的全局唯一标识符。
  • boolean isOpen():判断通道是否打开,若打开则可能在后续变得活跃。
  • boolean isRegistered():判断通道是否已注册到 EventLoop
  • boolean isActive():判断通道是否活跃且已连接。
2. 获取通道相关对象
  • EventLoop eventLoop():返回该通道注册到的 EventLoop
  • Channel parent():返回该通道的父通道,若没有则返回 null
  • ChannelConfig config():返回该通道的配置信息。
  • Unsafe unsafe():返回一个供内部使用的对象,提供不安全的操作。
  • ChannelPipeline pipeline():返回分配给该通道的 ChannelPipeline
  • ByteBufAllocator alloc():返回分配 ByteBuf 时使用的 ByteBufAllocator
3. 获取通道地址信息
  • SocketAddress localAddress():返回该通道绑定的本地地址,若未绑定则返回 null
  • SocketAddress remoteAddress():返回该通道连接的远程地址,若未连接则返回 null。对于能接收任意远程地址消息的通道(如 DatagramChannel),需使用 DatagramPacket#recipient() 来确定接收消息的来源。
4. 处理通道关闭
  • ChannelFuture closeFuture():返回一个 ChannelFuture,当该通道关闭时会得到通知,此方法总是返回相同的未来实例。
  • ChannelFuture close(ChannelPromise promise):关闭通道,并使用 ChannelPromise 来通知操作结果。
5. 判断通道可写性
  • boolean isWritable():判断 I/O 线程是否会立即执行请求的写操作。若返回 false,则任何写请求都会被排队,直到 I/O 线程准备好处理这些请求。
  • long bytesBeforeUnwritable():返回在 isWritable() 返回 false 之前还能写入的字节数,该值始终为非负,若 isWritable()false 则返回 0。
  • long bytesBeforeWritable():返回在 isWritable() 返回 true 之前,底层缓冲区必须释放的字节数,该值始终为非负,若 isWritable()true 则返回 0。
6. 执行 I/O 操作

Channel 接口继承自 ChannelOutboundInvoker,因此提供了一系列发起出站操作的方法,如绑定、连接、断开连接、关闭通道、读取数据等。以下是一些常用的方法:

  • ChannelFuture bind(SocketAddress localAddress):请求将通道绑定到指定的本地地址。
  • ChannelFuture connect(SocketAddress remoteAddress):请求将通道连接到指定的远程地址。
  • ChannelFuture disconnect():请求断开通道与远程对等方的连接。
  • ChannelFuture close():请求关闭通道。
  • Channel read():请求从通道读取数据到第一个入站缓冲区。
  • Channel flush():将通道的出站缓冲区中的数据刷新到网络。
  • ChannelFuture writeAndFlush(Object msg):将消息写入通道的出站缓冲区并刷新到网络。

7 io.netty.channel.ChannelHandler

ChannelHandler 接口用于处理 I/O 事件或拦截 I/O 操作,并将其转发到 ChannelPipeline 中的下一个处理器。它本身定义的方法不多,但通常需要实现其某个子类型来处理具体的 I/O 事件或操作。

public interface ChannelHandler {
    void handlerAdded(ChannelHandlerContext ctx) throws Exception;
    void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
    @Deprecated
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;

    @Inherited
    @Documented
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Sharable {
        // no value
    }
}
主要功能
1. 处理器添加与移除回调
  • void handlerAdded(ChannelHandlerContext ctx) throws Exception:当 ChannelHandler 被添加到实际的上下文并且准备好处理事件时调用。
  • void handlerRemoved(ChannelHandlerContext ctx) throws Exception:当 ChannelHandler 从实际的上下文移除并且不再处理事件时调用。
2. 异常捕获(已弃用)
  • void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception:如果抛出了 Throwable 异常,会调用此方法。不过现在建议实现 ChannelInboundHandler 并在其中实现该方法。
3. @Sharable 注解
  • 这是一个自定义注解,用于标记 ChannelHandler 实例是否可以被多次添加到一个或多个 ChannelPipeline 中而不会产生竞态条件。如果没有指定该注解,每次将处理器添加到管道时都必须创建一个新的处理器实例,因为它可能包含非共享状态(如成员变量)。
子类型

ChannelHandler 本身提供的方法有限,通常需要实现以下子类型来处理具体的 I/O 事件或操作:

  • ChannelInboundHandler:用于处理入站 I/O 事件,如通道注册、激活、读取数据等。
public interface ChannelInboundHandler extends ChannelHandler {
    void channelRegistered(ChannelHandlerContext ctx) throws Exception;
    void channelUnregistered(ChannelHandlerContext ctx) throws Exception;
    void channelActive(ChannelHandlerContext ctx) throws Exception;
    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 channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;
    @Override
    @SuppressWarnings("deprecation")
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}
  • ChannelOutboundHandler:用于处理出站 I/O 操作,如绑定、连接、写入数据等。

  • 适配器类

    :为了方便使用,Netty 还提供了以下适配器类:

    • ChannelInboundHandlerAdapter:处理入站 I/O 事件。
    • ChannelOutboundHandlerAdapter:处理出站 I/O 操作。
    • ChannelDuplexHandler:处理入站和出站事件。
上下文对象(ChannelHandlerContext

ChannelHandler 通过 ChannelHandlerContext 对象与所属的 ChannelPipeline 进行交互。使用上下文对象,ChannelHandler 可以将事件向上游或下游传递、动态修改管道,或者存储特定于处理器的信息(使用 AttributeKey)。

public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker {
    Channel channel();
    EventExecutor executor();
    String name();
    ChannelHandler handler();
    boolean isRemoved();
    // 其他方法...
}
状态管理

ChannelHandler 通常需要存储一些有状态的信息,有两种常见的方法:

1. 使用成员变量

推荐使用成员变量来存储处理器的状态。由于处理器实例的状态变量是专用于一个连接的,因此必须为每个新通道创建一个新的处理器实例,以避免竞态条件。

public class DataServerHandler extends SimpleChannelInboundHandler<Message> {
    private boolean loggedIn;

    @Override
    public void channelRead0(ChannelHandlerContext ctx, Message message) {
        if (message instanceof LoginMessage) {
            authenticate((LoginMessage) message);
            loggedIn = true;
        } else if (message instanceof GetDataMessage) {
            if (loggedIn) {
                ctx.writeAndFlush(fetchSecret((GetDataMessage) message));
            } else {
                fail();
            }
        }
    }
    // 其他方法...
}
2. 使用 AttributeKey

如果不想创建多个处理器实例,可以使用 ChannelHandlerContext 提供的 AttributeKey 来存储状态。这样,处理器的状态就与 ChannelHandlerContext 关联,可以将同一个处理器实例添加到不同的管道中。

@Sharable
public class DataServerHandler extends SimpleChannelInboundHandler<Message> {
    private final AttributeKey<Boolean> auth = AttributeKey.valueOf("auth");

    @Override
    public void channelRead(ChannelHandlerContext ctx, Message message) {
        Attribute<Boolean> attr = ctx.attr(auth);
        if (message instanceof LoginMessage) {
            authenticate((LoginMessage) message);
            attr.set(true);
        } else if (message instanceof GetDataMessage) {
            if (Boolean.TRUE.equals(attr.get())) {
                ctx.writeAndFlush(fetchSecret((GetDataMessage) message));
            } else {
                fail();
            }
        }
    }
    // 其他方法...
}

8 io.netty.channel.ChannelInitializer

这个类的主要作用是提供一种便捷的方式,在 Channel 注册到其 EventLoop 之后,对该 ChannelChannelPipeline 进行初始化设置。ChannelInitializer 被标记为 @Sharable,这意味着它的实现类实例可以安全地被添加到多个 ChannelPipeline 中,并且可以被多个 Channel 共享使用。

使用场景

在 Netty 应用程序开发中,我们经常需要为不同类型的 Channel 配置不同的 ChannelHandler 链,以实现各种功能,如数据编解码、业务逻辑处理等。ChannelInitializer 主要用于以下几种场景:

  • 服务器端:在 ServerBootstrapchildHandler 方法中使用,为每个新连接的客户端 Channel 初始化 ChannelPipeline
  • 客户端:在 Bootstraphandler 方法中使用,为客户端连接的 Channel 初始化 ChannelPipeline
工作原理

ChannelInitializer 实现了 ChannelInboundHandler 接口中的一些方法,主要是 channelRegisteredhandlerAdded 方法。当 Channel 注册到 EventLoop 时,会触发这些方法,进而调用抽象方法 initChannel 来初始化 ChannelPipeline

  • initChannel 方法:这是一个抽象方法,需要开发者在子类中实现。在该方法中,可以向 ChannelPipeline 中添加各种 ChannelHandler
  • 初始化流程:当 Channel 注册到 EventLoop 后,ChannelInitializerhandlerAdded 方法会被调用。如果此时 Channel 已经注册,会调用 initChannel 方法进行初始化,初始化完成后会将 ChannelInitializerChannelPipeline 中移除,避免重复初始化。
代码示例

以下是一个简单的使用 ChannelInitializer 的示例,展示了如何在服务器端和客户端初始化 ChannelPipeline

服务器端代码
public class NettyServer {
    private final int port;

    public NettyServer(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // 用于接受客户端连接
        EventLoopGroup workerGroup = new NioEventLoopGroup(); // 用于处理客户端读写操作
        try {
            ServerBootstrap b = new ServerBootstrap(); // 启动服务器的辅助类
            b.group(bossGroup, workerGroup)
              .channel(NioServerSocketChannel.class) // 指定使用 NIO 的服务器套接字通道
              .childHandler(new ChannelInitializer<SocketChannel>() {
                  @Override
                  public void initChannel(SocketChannel ch) throws Exception {
                      ch.pipeline().addLast(new StringDecoder()); // 添加字符串解码器
                      ch.pipeline().addLast(new StringEncoder()); // 添加字符串编码器
                      ch.pipeline().addLast(new ServerHandler()); // 添加自定义业务处理处理器
                  }
              })
              .option(ChannelOption.SO_BACKLOG, 128)          // 设置 TCP 缓冲区
              .childOption(ChannelOption.SO_KEEPALIVE, true); // 保持长连接

            // 绑定端口,开始接收进来的连接
            ChannelFuture f = b.bind(port).sync();

            // 等待服务器  socket 关闭 。
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new NettyServer(8080).run();
    }
}

9 io.netty.channel.ChannelPipeline

它用于管理 ChannelHandler 链,负责处理和拦截 Channel 的入站事件和出站操作。

1. 基本概念

ChannelPipeline 实现了拦截过滤器模式(Intercepting Filter Pattern),允许用户完全控制事件的处理方式以及管道中各个 ChannelHandler 之间的交互。每个 Channel 都有自己的 ChannelPipeline,并且在新 Channel 创建时会自动创建。

2. 事件流处理
入站事件(Inbound Events)

入站事件通常由 I/O 线程生成,例如读取远程对等方的数据。入站事件会按照 ChannelPipeline 中入站处理器的顺序从下往上处理。如果入站事件超出了最顶层的入站处理器,它将被静默丢弃,或者在需要关注时进行日志记录。

出站事件(Outbound Events)

出站事件通常由应用程序发起,例如写请求。出站事件会按照 ChannelPipeline 中出站处理器的顺序从上往下处理。如果出站事件超出了最底层的出站处理器,它将由与 Channel 关联的 I/O 线程处理,该线程通常会执行实际的输出操作。

3. 代码示例中的事件流图示
                                                 I/O Request
                                            via {@link Channel} or
                                        {@link ChannelHandlerContext}
                                                      |
  +---------------------------------------------------+---------------+
  |                           ChannelPipeline         |               |
  |                                                  \|/              |
  |    +---------------------+            +-----------+----------+    |
  |    | Inbound Handler  N  |            | Outbound Handler  1  |    |
  |    +----------+----------+            +-----------+----------+    |
  |              /|\                                  |               |
  |               |                                  \|/              |
  |    +----------+----------+            +-----------+----------+    |
  |    | Inbound Handler N-1 |            | Outbound Handler  2  |    |
  |    +----------+----------+            +-----------+----------+    |
  |              /|\                                  .               |
  |               .                                   .               |
  | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
  |        [ method call]                       [method call]         |
  |               .                                   .               |
  |               .                                  \|/              |
  |    +----------+----------+            +-----------+----------+    |
  |    | Inbound Handler  2  |            | Outbound Handler M-1 |    |
  |    +----------+----------+            +-----------+----------+    |
  |              /|\                                  |               |
  |               |                                  \|/              |
  |    +----------+----------+            +-----------+----------+    |
  |    | Inbound Handler  1  |            | Outbound Handler  M  |    |
  |    +----------+----------+            +-----------+----------+    |
  |              /|\                                  |               |
  +---------------+-----------------------------------+---------------+
                  |                                  \|/
  +---------------+-----------------------------------+---------------+
  |               |                                   |               |
  |       [ Socket.read() ]                    [ Socket.write() ]     |
  |                                                                   |
  |  Netty Internal I/O Threads (Transport Implementation)            |
  +-------------------------------------------------------------------+
4. 主要方法
添加处理器
  • addFirst(String name, ChannelHandler handler):将 ChannelHandler 插入到 ChannelPipeline 的开头。
  • addLast(String name, ChannelHandler handler):将 ChannelHandler 追加到 ChannelPipeline 的末尾。
  • addBefore(String baseName, String name, ChannelHandler handler):在指定名称的现有处理器之前插入 ChannelHandler
  • addAfter(String baseName, String name, ChannelHandler handler):在指定名称的现有处理器之后插入 ChannelHandler
移除处理器
  • remove(ChannelHandler handler):从 ChannelPipeline 中移除指定的 ChannelHandler
  • remove(String name):根据名称从 ChannelPipeline 中移除 ChannelHandler
  • remove(Class<T> handlerType):根据类型从 ChannelPipeline 中移除 ChannelHandler
替换处理器
  • replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler):用新的 ChannelHandler 替换指定的旧处理器。
5. 线程安全

ChannelPipeline 是线程安全的,这意味着可以在任何时候添加或移除 ChannelHandler。例如,在交换敏感信息时可以插入加密处理器,交换完成后再移除它。

6. 构建管道示例
ChannelPipeline pipeline = ch.pipeline();

pipeline.addLast("decoder", new MyProtocolDecoder());
pipeline.addLast("encoder", new MyProtocolEncoder());
pipeline.addLast("handler", new MyBusinessLogicHandler());

在这个示例中,MyProtocolDecoder 用于将二进制数据转换为 Java 对象,MyProtocolEncoder 用于将 Java 对象转换为二进制数据,MyBusinessLogicHandler 用于执行实际的业务逻辑。

7. 代码实现

DefaultChannelPipelineChannelPipeline 的默认实现,它使用双向链表来管理 ChannelHandler。以下是 DefaultChannelPipeline 的部分关键代码:

public class DefaultChannelPipeline implements ChannelPipeline {
    final HeadContext head;
    final TailContext tail;

    protected DefaultChannelPipeline(Channel channel) {
        this.channel = ObjectUtil.checkNotNull(channel, "channel");
        tail = new TailContext(this);
        head = new HeadContext(this);

        head.next = tail;
        tail.prev = head;
    }

    // 添加处理器的方法实现
    @Override
    public final ChannelPipeline addFirst(String name, ChannelHandler handler) {
        return addFirst(null, name, handler);
    }

    private ChannelPipeline internalAdd(EventExecutorGroup group, String name,
                                        ChannelHandler handler, String baseName,
                                        AddStrategy addStrategy) {
        // ...
    }

    // 其他方法...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值