在之前的文章中我们学习了 netty的io模型、netty的使用分析、netty线程模型、netty的ChannelPipeline类学习、netty的ChannelPromise类学习。下面我们从使用netty例子出发探究一下netty的工作流程(其中包括服务端和客户端的创建、一次完整的请求和响应)。
1. AbstractBootstrap类简介
在探究netty一次请求的完整流程之前我们先来了解一下XXXBootstrap组件。 它是netty为我们提供的便于我们使用的工具启动类。他以builder的模式让我们可以简单设置例如channel、TCP参数、相关处理器。其中客户端使用Bootstrap类,服务端使用ServerBootstrap类两者的基类为AbstractBootstrap类。
1.1. 核心方法
AbstractBootstrap的
- bind():netty服务端的与端口绑定,也是建立netty服务端的核心方法
Bootstrap的
- connect():netty客户端与服务端进行连接,也是建立netty客户端的核心方法
补充说明:netty服务底层是使用NIO原理实现的,所以此处分析netty相关源码我们也分析到NIO层,至于NIO的底层原理实现请期待笔者后续的博文。
2. netty服务端创建
2.1. 服务端启动入口
netty服务端创建相关代码如下,服务端创建是使用bind()核心方法,下面来分析bind方法
//一、netty 服务端启动
//.......省略ServerBootstrap构建过程
//绑定端口 netty服务端创建
ChannelFuture sync = serverBootstrap.bind(port).sync();
//服务监听端口关闭
sync.channel().closeFuture().sync();
bind()方法最终调用io.netty.bootstrap.AbstractBootstrap#doBind()方法,下面我们来分析netty服务端启动方法。
2.2. doBind()方法
private ChannelFuture doBind(final SocketAddress localAddress) {
//正如其方法名一样 初始化channel,并进行channel和selector(多路复用器的注册绑定)
//其返回值是netty自己封装的异步结果接口ChannelFuture
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
//如果异步结果中有异常则直接返回 此时:netty服务端初始化失败
if (regFuture.cause() != null) {
return regFuture;
}
final ChannelPromise promise;
//上述的initAndRegister的结果是异步通知的 所有此处需要进行条件分支判断
//如果上述操作已经完成(通道初始化完成)则直接进行doBind0()方法
if (regFuture.isDone()) {
promise = channel.newPromise();
//channel和端口进行绑定操作
doBind0(regFuture, channel, localAddress, promise);
} else {
//一般情况下initAndRegister会很快完成并给出结果 但是万一呢?
promise = new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE);
//此处针对如果上述未完成 添加一个事件监听,待上述操作完成后触发doBind0()
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
doBind0(regFuture, channel, localAddress, promise);
}
});
}
return promise;
}
netty服务端启动和绑定过程并不复杂主要分成了两部分(两个核心方法)
- 初始化: initAndRegister方法,该方法主要功能是创建Channel对象(网络请求的数据流通道),并整合ServerBootstrap相关配置比如工作线程(NioEventLoopGroup)、option(参数配置)、attr(属性)、添加相关处理器(ChannelHandler),最后将多路复用器Selector注册到Channel。
- 端口绑定:doBind0方法,该方法主要将端口和channel进行绑定。
2.3. netty服务初始化
2.3.1. initAndRegister()方法
final ChannelFuture initAndRegister() {
Channel channel;
try {
//1、创建网络通道Channel
channel = createChannel();
} catch (Throwable t) {
return VoidChannel.INSTANCE.newFailedFuture(t);
}
try {
//2、为channel配置 线程、option、attr、事件处理器ChannelHandler
init(channel);
} catch (Throwable t) {
channel.unsafe().closeForcibly();
return channel.newFailedFuture(t);
}
ChannelPromise regFuture = channel.newPromise();
//channel注册多路复用器selector
channel.unsafe().register(regFuture);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
netty服务端初始化也可以分成三部分
- 创建netty的channel对象,因为我们在构建的ServerBootstrap对象的时候调用了channel方法传入NioServerSocketChannel.class
//调用channel(NioServerSocketChannel.class)方法 会创建ServerBootstrapChannelFactory
return channelFactory(new ServerBootstrapChannelFactory<ServerChannel>(channelClass));
createChannel方法调用ServerBootstrapChannelFactory的newChannel(),通过反射调用NioServerSocketChannel的构造函数
//构建函数
public NioServerSocketChannel(EventLoop eventLoop, EventLoopGroup childGroup) {
//父构造函数 newSocket()创建一个NIO的ServerSocketChannel 和eventLoop线程以及工作线程childGroup
//包装成NioServerSocketChannel
super(null, eventLoop, childGroup, newSocket(), SelectionKey.OP_ACCEPT);
config = new DefaultServerSocketChannelConfig(this, javaChannel().socket());
}
- 初始化channel配置:init()方法整合ServerBootstrap相关配置比如工作线程(NioEventLoopGroup)、option(参数配置)、attr(属性)、添加相关处理器(ChannelHandler)并配置childHandler、currentChildOptions、currentChildAttrs包装成ServerBootstrapAcceptor
- channel注册selector:通过channel的unsafe对象(io.netty.channel.AbstractChannel.AbstractUnsafe)的register方法。
2.3.2. init()方法
在上述方法的第二部分init()方法初始化channel配置最终实现类为io.netty.bootstrap.ServerBootstrap#init。
void init(Channel channel) throws Exception {
//获取AbstractBootstrap的options(TCP配置信息) 设置到ChannelConfig中
final Map<ChannelOption<?>, Object> options = options();
synchronized (options) {
channel.config().setOptions(options);
}
//获取AbstractBootstrap的attr属性 设置到Attribute中
final Map<AttributeKey<?>