客户端 BootStrap
Bootstrap 是 Netty 提供的一个便利的工厂类, 我们可以通过它来完成 Netty 的客户端或服务器端的 Netty 初始化。
Channel 简介
Netty 中,
Channel
是一个 Socket 的抽象。每当 Netty 建立了一个连接后, 都创建一个对应的 Channel 实例。
不同的 Channel 类型
除了 TCP 协议以外,Netty 还支持很多其他的连接协议, 并且每种协议还有 NIO(非阻塞 IO)和 OIO(Old-IO, 即传统的 阻塞 IO)版本的区别。不同协议不同的阻塞类型的连接都有不同的 Channel 类型与之对应。
类名 | Value |
---|---|
NioSocketChannel | 异步非阻塞的客户端 TCP Socket 连接。 |
NioServerSocketChannel | 异步非阻塞的服务器端 TCP Socket 连接。 |
OioSocketChannel | 同步阻塞的客户端 TCP Socket 连接。 |
OioServerSocketChannel | 同步阻塞的服务器端 TCP Socket 连接。 |
Channel 的生命周期
客户端代码实现
大致可以分为五步:
- 创建EventLoopGroup
- 创建Bootstrap并设置相关参数:
1)channel类型,客户端:NioSocketChannel,服务端:NioServerSocketChannel
2)设置option参数,
3)设置handler处理器 - connect服务器ip,端口 并启动
public class ChatClient {
public ChatClient connect(int port, String host, final String nickName) {
EventLoopGroup group = new NioEventLoopGroup(); // 指定一个 NIO 的 EventLoopGroup
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class) // 初始化ReflectiveChannelFactory对象,NioSocketChannel作为其属性
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() { // 设置处理数据的 Handler
@Override
protected void initChannel(SocketChannel ch) throws Exception {
...
}
});
ChannelFuture channelFuture = bootstrap.connect(host, port).sync(); //发起同步连接操作
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
group.shutdownGracefully(); //关闭,释放线程资源
}
return this;
}
public static void main(String[] args) {
new ChatClient().connect(8080, "localhost", "Tom");
}
}
EventLoopGroup 的初始化
Netty幕后使用EventLoop,用EventLoop分配给每一个Channel来处理所有的事件,包括如下:
1)注册所有自己关注的事件
2)分发或者调度对应的事件给那些注册该事件的ChannelHandlers
3)指定下一个处理流程
EventLoop本身就是仅有一个线程去驱动的,但是它可以管控一个Channel的所有I/O事件,并且EventLoop整个生命周期不会变更,仅此一个线程实例。
NioEventLoopGroup
有几个重载的构造器,不过最终都是调用的父类 MultithreadEventLoopGroup
的构造器。
MultithreadEventLoopGroup.class
如果入参线程数
nThreads
=0,那么 Netty 会设置默认的线程数DEFAULT_EVENT_LOOP_THREADS
。
DEFAULT_EVENT_LOOP_THREADS:如果系统属性中没有设置io.netty.eventLoopThreads
,则返回(处理器核心数 * 2
)。
private static final int DEFAULT_EVENT_LOOP_THREADS =
Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
……
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
MultithreadEventExecutorGroup.class
new EventExecutor[nThreads]
,创建一个大小为 nThreads 的 SingleThreadEventExecutor 数组。this.newChild((Executor)executor, args)
,初始化 children 数组。chooserFactory.newChooser(this.children)
,根据nThreads
的大小,创建不同的Chooser
,即如果 nThreads 是 2 的幂,则使用 PowerOfTwoEventExecutorChooser,反之使用 GenericEventExecutorChooser。children
数组是Chooser的属性。
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) {
……
if (nThreads <= 0) {
……
} else {
……
this.children = new EventExecutor[nThreads]; // 创建一个大小为 nThreads 的 SingleThreadEventExecutor 数组
int j;
for(int i = 0; i < nThreads; ++i) {
boolean success = false;
boolean var18 = false;
try {
var18 = true;
this.children[i] = this.newChild((Executor)executor, args); // 返回一个 EventExecutor 实例,初始化 children 数组,
success = true;
var18 = false;
} catch (Exception var19) {
……
} finally {
……
}
……
}
this.chooser = chooserFactory.newChooser(this.children); // 从 children 数组中选出一个合适的 EventExecutor 实例
……
}
}
EventLoopGroup 的初始化过程总结:
- 设置
nThreads
大小,如果没有指定,则取值:处理器核心数 * 2
。 EventLoopGroup
的父类MultithreadEventExecutorGroup
内部维护了一个大小是nThreads
的EventExecutor[] children
,构成了一个线程池。newChild()
是一个抽象方法,由子类NioEventLoopGroup
初始化children 数组
,每个元素都是NioEventLoop
。- 根据
nThreads
的大小,创建不同的Chooser
(PowerOfTwoEventExecutorChooser
:GenericEventExecutorChooser
)。
Bootstrap 的初始化
bootstrap.channel(NioSocketChannel.class)
初始化
ReflectiveChannelFactory.class
,NioSocketChannel.class
作为其属性
AbstractBootstrap.class:
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
} else {
return this.channelFactory((
io.netty.channel.ChannelFactory)(new ReflectiveChannelFactory(channelClass)));
}
}
ReflectiveChannelFactory.class
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Class<? extends T> clazz;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
} else {
this.clazz = clazz;
}
}
public T newChannel() {
try {
return (Channel)this.clazz.newInstance();
} catch (Throwable var2) {
throw new ChannelException("Unable to create Channel from class " + this.clazz, var2);
}
}
public String toString() {
return StringUtil.simpleClassName(this.clazz) + ".class";
}
}
ReflectiveChannelFactory.newChannel()何时调用?
newChannel()用来创建
NioSocketChannel
的实例,所以NioSocketChannel
的实例何时创建?
bootstrap.connect(SERVER_HOST, port)的
initAndRegister
方法中,调用了newChannel
方法。
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = this.channelFactory.newChannel();
this.init(channel);
} catch (Throwable var3) {
……
}
……
return regFuture;
}
客户端 Channel 的初始化总结
- 创建
ReflectiveChannelFactory
实例,用于封装NioSocketChannel.class
,并提供newChannel()
方法,用于反射出NioSocketChannel
实例。
bootstrap.option
设置通道的选项参数, 对于服务端而言就是ServerSocketChannel, 客户端而言就是SocketChannel;
bootstrap.option(ChannelOption.TCP_NODELAY, true)
bootstrap.handler(handler处理器)
handler方法主要就是添加一个ChannelHandler ,
ChannelInitializer
实现了ChannelHandler
接口,重写initChannel()。
先将ChannelInitializer加入到
Pipeline
。 通过ChannelInitializer的回调方法handlerAdded()
,调用 initChannel()方法,最终将各种 handler 添加到ChannelPipeline 中,实现handler的链式调用
。
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline()
.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4))
.addLast("frameEncoder", new LengthFieldPrepender(4))
.addLast("encoder", new ObjectEncoder())
.addLast("decoder",
new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)))
}
})
ChannelHandlerContext
每个 ChannelHandler 被添加到 ChannelPipeline 后,都会创建一个 ChannelHandlerContext 并与之创建的 ChannelHandler 关联绑定。
ChannelHandlerContext、ChannelHandler、ChannelPipeline 的关系:
Bootstrap 的连接(bootstrap.connect)
AbstractBootstrap.class #doResolveAndConnect:
private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
ChannelFuture regFuture = this.initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.isDone()) {
return !regFuture.isSuccess() ? regFuture : this.doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
} else {
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
promise.registered();
Bootstrap.this.doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
}
}
});
return promise;
}
}
initAndRegister channel
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = this.channelFactory.newChannel();
this.init(channel);
} catch (Throwable var3) {
……
}
ChannelFuture regFuture = this.config().group().register(channel);
……
return regFuture;
}
newChannel()
获取SocketChannel 的实例,详见 《NioSocketChannel 的实例》
init(channel)
对Channle进行初始化配置。
- 创建pipeline
- 添加Handler到链表末端
- 获取options参数
- 将options参数赋值到前面初始化的NioSocketChannelConfig对象里
void init(Channel channel) throws Exception {
ChannelPipeline p = channel.pipeline(); // 创建pipeline
p.addLast(new ChannelHandler[]{this.config.handler()}); // 添加Handler到链表末端
Map<ChannelOption<?>, Object> options = this.options0(); // 获取options参数
synchronized(options) {
Iterator i$ = options.entrySet().iterator();
while(true) {
if (!i$.hasNext()) {
break;
}
Entry e = (Entry)i$.next();
try { // 将options参数赋值到前面初始化的NioSocketChannelConfig对象里
if (!channel.config().setOption((ChannelOption)e.getKey(), e.getValue())) {
logger.warn("Unknown channel option: " + e);
}
} catch (Throwable var10) {
logger.warn("Failed to set a channel option: " + channel, var10);
}
}
}
Map<AttributeKey<?>, Object> attrs = this.attrs0();
synchronized(attrs) {
Iterator i$ = attrs.entrySet().iterator();
while(i$.hasNext()) {
Entry<AttributeKey<?>, Object> e = (Entry)i$.next();
channel.attr((AttributeKey)e.getKey()).set(e.getValue());
}
}
}
register(channel)
- 将
Channel
与对应的EventLoop
关联。在 Netty 中,每个 Channel 都会关联一个特定的 EventLoop,并且这个 Channel 中的所有 IO 操作都是在这个 EventLoop 中执行的; - 关联好 Channel 和 EventLoop 后,通过unsafe 的 register方法,最终通过底层 Java NIO(AbstractNioChannel.class) 的
SocketChannel
对象的 register()方法,将底层 Java NIO 的 SocketChannel 注册到指定的selector
中。
AbstractNioChannel.class
protected void doRegister() throws Exception {
boolean selected = false;
while(true) {
try { // 注册
this.selectionKey = this.javaChannel().register(this.eventLoop().selector, 0, this);
return;
} catch (CancelledKeyException var3) {
if (selected) {
throw var3;
}
this.eventLoop().selectNow();
selected = true;
}
}
}
doResolveAndConnect0
最终通过底层 Java NIO 的
SocketChannel.class
的doConnect方法
完成 NIO 底层的Socket
的连接。
DefaultChannelPipeline.HeadContext #connect
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
this.unsafe.connect(remoteAddress, localAddress, promise);
}
NioSocketChannel.class
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
……
try {
boolean connected = this.javaChannel().connect(remoteAddress);
……
} finally {
……
}
return var5;
}
客户端 BootStrap 发起连接请求的流程,时序图:
NioSocketChannel 的实例
newChannel()
方法中,clazz.newInstance()
利用反射机制创建 Channel 实例,相当于调用 NioSocketChannel 的默认构造器。
NioSocketChannel.class
public NioSocketChannel() {
this(DEFAULT_SELECTOR_PROVIDER);
}
public NioSocketChannel(SelectorProvider provider) {
this(newSocket(provider)); // 打开一个新的 Java NIO 的 SocketChannel
}
public NioSocketChannel(java.nio.channels.SocketChannel socket) {
this((Channel)null, socket);
}
public NioSocketChannel(Channel parent, java.nio.channels.SocketChannel socket) {
super(parent, socket);
this.config = new NioSocketChannel.NioSocketChannelConfig(this, socket.socket());
}
SelectorProvider.class
打开一个新的 Java NIO 的 SocketChannel。
private static java.nio.channels.SocketChannel newSocket(SelectorProvider provider) {
try {
return provider.openSocketChannel(); // 打开一个新的 Java NIO 的 SocketChannel
} catch (IOException var2) {
throw new ChannelException("Failed to open a socket.", var2);
}
}
AbstractNioByteChannel.class
设置:SelectionKey.OP_READ
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, 1); // SelectionKey.OP_READ = 1
}
AbstractChannel.class
初始化
unsafe
pipeline
protected AbstractChannel(Channel parent) {
this.parent = parent;
this.id = this.newId();
this.unsafe = this.newUnsafe();
this.pipeline = this.newChannelPipeline();
}
Unsafe 的初始化
unsafe 特别关键,它封装了对 Java 底层 Socket 的操作,是Netty 和 Java 底层沟通的重要桥梁。
AbstractChannel.class
protected AbstractChannel(Channel parent) {
this.parent = parent;
this.id = this.newId();
this.unsafe = this.newUnsafe();
this.pipeline = this.newChannelPipeline();
}
……
protected abstract AbstractChannel.AbstractUnsafe newUnsafe();
NioSocketChannel.class
返回一个 NioSocketChannelUnsafe 实例。
protected AbstractNioUnsafe newUnsafe() {
return new NioSocketChannel.NioSocketChannelUnsafe(); // 返回一个 NioSocketChannelUnsafe 实例
}
Pipeline 的初始化
在 Netty 中,一个
Channel
包含了一个ChannelPipeline
,而 ChannelPipeline 中又维护了一个由 ChannelHandlerContext 组成的双向链表
。这个链表的头是HeadContext
,链表的尾是TailContext
,并且每个 ChannelHandlerContext 中又关联着一个ChannelHandler
。
AbstractChannel.class
protected AbstractChannel(Channel parent) {
this.parent = parent;
this.id = this.newId();
this.unsafe = this.newUnsafe();
this.pipeline = this.newChannelPipeline();
}
……
protected DefaultChannelPipeline newChannelPipeline() {
return new DefaultChannelPipeline(this);
}
DefaultChannelPipeline.class
DefaultChannelPipeline
的构造器需要传入一个 channel,其实就是NioSocketChannel
。
protected DefaultChannelPipeline(Channel channel) {
this.channel = (Channel)ObjectUtil.checkNotNull(channel, "channel");
this.succeededFuture = new SucceededChannelFuture(channel, (EventExecutor)null);
this.voidPromise = new VoidChannelPromise(channel, true);
this.tail = new DefaultChannelPipeline.TailContext(this);
this.head = new DefaultChannelPipeline.HeadContext(this);
this.head.next = this.tail;
this.tail.prev = this.head;
}
Pipeline 的事件传播机制(Inbound 事件和 Outbound 事件)
Netty 中的传播事件可以分为两种:Inbound 事件和 Outbound 事件。
Outbound 事件总结:
- Outbound 事件是请求事件(由 connect()发起一个请求,并最终由 unsafe 处理这个请求)。
- Outbound 事件的发起者是 Channel。
- Outbound 事件的处理者是 unsafe。
- Outbound 事件在 Pipeline 中的传输方向是 tail -> head。
- 在 ChannelHandler 中处理事件时,如果这个 Handler 不是最后一个 Handler,则需要调用 ctx 的方法(如: ctx.connect()方法)将此事件继续传播下去。如果不这样做,那么此事件的传播会提前终止。
- Outbound 事件流:Context.OUT_EVT() -> Connect.findContextOutbound() -> nextContext.invokeOUT_EVT() -> nextHandler.OUT_EVT() -> nextContext.OUT_EVT()
Inbound 事件总结:
- Inbound 事件是通知事件,当某件事情已经就绪后,通知上层。
- Inbound 事件发起者是 unsafe。
- Inbound 事件的处理者是 Channe,如果用户没有实现自定义的处理方法,那么 Inbound 事件默认的处理者是 TailContext,并且其处理方法是空实现。
- Inbound 事件在 Pipeline 中传输方向是 head -> tail。
- 在 ChannelHandler 中处理事件时,如果这个 Handler 不是最后一个 Handler,则需要调用 ctx.fireIN_EVT()事 件(如:ctx.fireChannelActive()方法)将此事件继续传播下去。如果不这样做,那么此事件的传播会提前终止。
- Outbound 事件流:Context.fireIN_EVT() -> Connect.findContextInbound() -> nextContext.invokeIN_EVT() -> nextHandler.IN_EVT() -> nextContext.fireIN_EVT().
一个事件可以被一系列的事件处理器处理: