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 是否为无效的未来,如果是,则不允许调用一些特定的方法,如 addListener、await 等。
最佳实践建议
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 的实现类,如 CompleteChannelFuture、FailedChannelFuture、SucceededChannelFuture 等,用于表示不同状态的 ChannelFuture。这些实现类在内部维护了 ChannelFuture 的状态和结果,并实现了接口定义的各种方法。
2 io.netty.channel.EventLoop
它主要负责处理 Channel 的所有 I/O 操作。下面将从接口定义、主要功能、核心方法、与相关类的关系以及使用示例几个方面对 EventLoop 进行详细解析。
接口定义
public interface EventLoop extends OrderedEventExecutor, EventLoopGroup {
@Override
EventLoopGroup parent();
}
从接口定义可以看出,EventLoop 继承自 OrderedEventExecutor 和 EventLoopGroup。OrderedEventExecutor 确保任务按顺序执行,而 EventLoopGroup 表示一组 EventLoop。parent() 方法用于返回该 EventLoop 所属的 EventLoopGroup。
主要功能
- I/O 操作处理:
EventLoop的核心功能是处理Channel的 I/O 操作,包括连接建立、数据读写、连接关闭等。它负责监控Channel的状态变化,并在有 I/O 事件发生时执行相应的处理逻辑。 - 任务调度:除了处理 I/O 操作,
EventLoop还可以调度和执行普通的任务。这些任务可以是用户自定义的定时任务或异步任务。 - 线程管理:每个
EventLoop通常与一个线程关联,确保Channel的操作在同一个线程中执行,避免了多线程并发带来的复杂性和同步问题。
核心方法
虽然 EventLoop 接口中只定义了 parent() 方法,但它继承了 OrderedEventExecutor 和 EventLoopGroup 的许多重要方法:
继承自 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);
与相关类的关系
- EventLoopGroup:
EventLoop是EventLoopGroup的成员,一个EventLoopGroup可以包含多个EventLoop。EventLoopGroup负责管理和分配EventLoop,并提供了统一的接口来操作这些EventLoop。 - Channel:
EventLoop与Channel紧密相关,每个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);
}
主要功能
- 继承自
EventExecutorGroup:EventLoopGroup继承自EventExecutorGroup,这意味着它拥有EventExecutorGroup的所有功能,如任务执行、任务调度、生命周期管理等。 - 获取下一个
EventLoop:next()方法用于返回下一个可用的EventLoop实例。在多线程环境下,EventLoopGroup会负责管理多个EventLoop实例,并根据一定的策略(如轮询)选择一个EventLoop来处理新的Channel。 Channel注册:register()方法用于将Channel注册到EventLoop中。注册完成后,Channel的所有 I/O 操作都将由该EventLoop负责处理。注册操作是异步的,返回的ChannelFuture可以用于监听注册操作的完成状态。
主要实现类
-
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 {
// ...
}
-
EpollEventLoopGroup基于 Linux 的epoll机制实现的EventLoopGroup,只能在 Linux 系统上使用。它提供了比 NIO 更高的性能,特别适用于高并发的网络编程场景。
@Deprecated public final class EpollEventLoopGroup extends MultiThreadIoEventLoopGroup { // ... } -
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中下一个ChannelOutboundHandler的bind方法。 - 参数:
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中下一个ChannelOutboundHandler的connect方法。 - 参数:
remoteAddress:要连接的远程地址。localAddress:可选的本地地址。promise:用于通知操作完成的ChannelPromise对象。
- 返回值:一个
ChannelFuture对象,用于监听连接操作的完成状态。
3. 断开连接操作
default ChannelFuture disconnect() {
return disconnect(newPromise());
}
ChannelFuture disconnect(ChannelPromise promise);
- 功能:请求断开
Channel与远程对等方的连接。断开连接操作会触发ChannelPipeline中下一个ChannelOutboundHandler的disconnect方法。 - 参数:
promise:用于通知操作完成的ChannelPromise对象。
- 返回值:一个
ChannelFuture对象,用于监听断开连接操作的完成状态。
4. 关闭操作
default ChannelFuture close() {
return close(newPromise());
}
ChannelFuture close(ChannelPromise promise);
- 功能:请求关闭
Channel。关闭后,Channel不能再被重用。关闭操作会触发ChannelPipeline中下一个ChannelOutboundHandler的close方法。 - 参数:
promise:用于通知操作完成的ChannelPromise对象。
- 返回值:一个
ChannelFuture对象,用于监听关闭操作的完成状态。
5. 取消注册操作
default ChannelFuture deregister() {
return deregister(newPromise());
}
ChannelFuture deregister(ChannelPromise promise);
- 功能:请求将
Channel从之前分配的EventExecutor中取消注册。取消注册操作会触发ChannelPipeline中下一个ChannelOutboundHandler的deregister方法。 - 参数:
promise:用于通知操作完成的ChannelPromise对象。
- 返回值:一个
ChannelFuture对象,用于监听取消注册操作的完成状态。
6. 读取操作
ChannelOutboundInvoker read();
- 功能:请求从
Channel读取数据到第一个入站缓冲区。如果读取到数据,会触发ChannelInboundHandler的channelRead事件;读取完成后,会触发channelReadComplete事件。读取操作会触发ChannelPipeline中下一个ChannelOutboundHandler的read方法。 - 返回值:返回
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
它继承自 ChannelFuture 和 Promise<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的状态变为成功或失败。sync和await方法会抛出InterruptedException,如果线程在等待过程中被中断;syncUninterruptibly和awaitUninterruptibly方法则不会抛出该异常。 - 示例:
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关联的Channel。unvoid方法用于将一个void类型的ChannelPromise转换为普通的ChannelPromise。
#### 主要实现类
1. DefaultChannelPromise
public class DefaultChannelPromise extends DefaultPromise<Void> implements ChannelPromise, FlushCheckpoint {
// ...
}
- 功能:
DefaultChannelPromise是ChannelPromise的默认实现类。它继承自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 之后,对该 Channel 的 ChannelPipeline 进行初始化设置。ChannelInitializer 被标记为 @Sharable,这意味着它的实现类实例可以安全地被添加到多个 ChannelPipeline 中,并且可以被多个 Channel 共享使用。
使用场景
在 Netty 应用程序开发中,我们经常需要为不同类型的 Channel 配置不同的 ChannelHandler 链,以实现各种功能,如数据编解码、业务逻辑处理等。ChannelInitializer 主要用于以下几种场景:
- 服务器端:在
ServerBootstrap的childHandler方法中使用,为每个新连接的客户端Channel初始化ChannelPipeline。 - 客户端:在
Bootstrap的handler方法中使用,为客户端连接的Channel初始化ChannelPipeline。
工作原理
ChannelInitializer 实现了 ChannelInboundHandler 接口中的一些方法,主要是 channelRegistered 和 handlerAdded 方法。当 Channel 注册到 EventLoop 时,会触发这些方法,进而调用抽象方法 initChannel 来初始化 ChannelPipeline。
initChannel方法:这是一个抽象方法,需要开发者在子类中实现。在该方法中,可以向ChannelPipeline中添加各种ChannelHandler。- 初始化流程:当
Channel注册到EventLoop后,ChannelInitializer的handlerAdded方法会被调用。如果此时Channel已经注册,会调用initChannel方法进行初始化,初始化完成后会将ChannelInitializer从ChannelPipeline中移除,避免重复初始化。
代码示例
以下是一个简单的使用 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. 代码实现
DefaultChannelPipeline 是 ChannelPipeline 的默认实现,它使用双向链表来管理 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) {
// ...
}
// 其他方法...
}
528

被折叠的 条评论
为什么被折叠?



