目录
netty版本:4.1.30.Final
四个基本事件
Selector是什么?
SelectionKey是什么?
NioServerSocketChannel源码
NioSocketChannel源码
NioEventLoop是什么?
netty服务端启动
服务端整体逻辑架构
参考文档:
OP_READ = 1 00000001 读
OP_WRITE = 4 00000100 写
OP_CONNECT = 8 00001000 连接
OP_ACCEPT = 16 00010000 接受连接
如果一个SelectionKey感兴趣读, 那么其interestOps=00000001
如果一个SelectionKey感兴趣写, 那么其interestOps=00000100
如果一个SelectionKey感兴趣连接, 那么其interestOps=00001000
如果一个SelectionKey感兴趣接受连接,那么其interestOps=00010000
如果感兴趣多个事件:java运算符 与(&)、非(~)、或(|)、异或(^)
读\写:00000001 | 00000100 = 00000101
读\写\连接\接受连接:00000001 | 00000100 | 00001000 | 00010000 = 00011101
判断自己是否有关注某个事件:
int interestSet = selectionKey.interestOps();
boolean isInterestedAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
boolean isInterestedConnect = (interestSet & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT;
boolean isInterestedInRead = (interestSet & SelectionKey.OP_READ) == SelectionKey.OP_READ;
boolean isInterestedInWrite = (interestSet & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE;
它是一个抽象类,看它一个子类:SelectorImpl,它维护了一个Set
Selector几个重载的select()方法:
Selector的wakeUp方法:
它是一个抽象类,看它一个子类:SelectionKeyImpl,看它的定义:
public class SelectionKeyImpl extends AbstractSelectionKey {
final SelChImpl channel;
public final SelectorImpl selector;
private int index;
private volatile int interestOps;
private int readyOps;
......
}
可以看到,一个SelectionKey中包含一个channel、一个Selector、该channel感兴趣的事件集合interestOps,SelectionKey就是标识该channel注册在某一个selector上
public class NioServerSocketChannel extends AbstractNioMessageChannel
implements io.netty.channel.socket.ServerSocketChannel {
@Override
protected int doReadMessages(List
首先通过ServerSocketChannel的accept()方法接受新的客户端请求,如果SocketChannel不为空,则利用当前的NioServerSocketChannel、EventLoop和SocketChannel创建新的NioSocketChannel,并加入到List
public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
@Override
protected SocketChannel javaChannel() {
return (SocketChannel) super.javaChannel();
}
@Override
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
if (localAddress != null) {
javaChannel().socket().bind(localAddress);
}
boolean success = false;
try {
boolean connected = javaChannel().connect(remoteAddress);
if (!connected) {
selectionKey().interestOps(SelectionKey.OP_CONNECT);
}
success = true;
return connected;
} finally {
if (!success) {
doClose();
}
}
}
}
先调用javaChannel().socket().bind(localAddress)绑定本地地址,若成功则:调用javaChannel().connect(remoteAddress)发起TCP连接
nioEventloop其内部(父类)持有一个thread对象,而该nioEventloop的run方法正是由该线程执行的
nioEventloop还持有两个最重要的对象--selector和queue,前者就是我们的多路复用器,后者则是存放我们的任务队列
分析nioEventloop的run方法:
@Override
protected void run() {
for (;;) {
try {
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.SELECT:
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
default:
}
cancelledKeys = 0;
needsToSelectAgain = false;
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} catch (Throwable t) {
handleLoopException(t);
}
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
private void processSelectedKeysOptimized() {
for (int i = 0; i < selectedKeys.size; ++i) {
final SelectionKey k = selectedKeys.keys[i];
// null out entry in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.keys[i] = null;
final Object a = k.attachment();
if (a instanceof AbstractNioChannel) {
processSelectedKey(k, (AbstractNioChannel) a);
} else {
@SuppressWarnings("unchecked")
NioTask task = (NioTask) a;
processSelectedKey(k, task);
}
if (needsToSelectAgain) {
// null out entries in the array to allow to have it GC'ed once the Channel close
// See https://github.com/netty/netty/issues/2363
selectedKeys.reset(i + 1);
selectAgain();
i = -1;
}
}
}
遍历该EventLoop持有的selector的selectedKeys:最终调用的是:
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
return;
}
if (eventLoop != this || eventLoop == null) {
return;
}
unsafe.close(unsafe.voidPromise());
return;
}
try {
int readyOps = k.readyOps();
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
ch.unsafe().forceFlush();
}
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
这个就已经是我们熟悉的NIO中的类似操作读写连接事件的io
处理逻辑里面还有一个else:processSelectedKey(k, task);暂时不看了,详情见:task执行
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 创建并初始化 Netty 服务端 Bootstrap 对象
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup);
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.childHandler(new ChannelInitializer() {
@Override
public void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
/**
* 收到客户端请求后,先执行解码RpcDecoder,在进入RpcServerHandler执行
* 执行完成后,由RpcEncoder编码后,返回给客户端
*/
//我是RpcDecoder,属于ChannelInboundHandlerAdapter,先add先执行
pipeline.addLast(new RpcDecoder(RpcRequest.class)); // 解码 RPC 请求
//我是RpcEncoder,属于ChannelOutboundHandlerAdapter,先add却要后执行
pipeline.addLast(new RpcEncoder(RpcResponse.class)); // 编码 RPC 响应
//我是RpcServerHandler,属于ChannelInboundHandlerAdapter,先add先执行,所以我在RpcDecoder之后执行
pipeline.addLast(new RpcServerHandler(handlerMap)); // 处理 RPC 请求
}
});
bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
// 获取 RPC 服务器的 IP 地址与端口号
String[] addressArray = StringUtil.split(serviceAddress, ":");
String ip = addressArray[0];
int port = Integer.parseInt(addressArray[1]);
// 启动 RPC 服务器
ChannelFuture future = bootstrap.bind(ip, port).sync();
// 注册 RPC 服务地址
if (serviceRegistry != null) {
for (String interfaceName : handlerMap.keySet()) {
serviceRegistry.register(interfaceName, serviceAddress);
LOGGER.debug("register service: {} => {}", interfaceName, serviceAddress);
}
}
LOGGER.debug("server started on port {}", port);
// 关闭 RPC 服务器
future.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
从ChannelFuture future = bootstrap.bind(ip, port).sync()开始分析:调用的是:AbstractBootstrap.bind,最后调用AbstractBootstrap的这个方法:
private ChannelFuture doBind(final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
}
if (regFuture.isDone()) {
// At this point we know that the registration was complete and successful.
ChannelPromise promise = channel.newPromise();
doBind0(regFuture, channel, localAddress, promise);
return promise;
} else {
// Registration future is almost always fulfilled already, but just in case it's not.
final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
// Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
// IllegalStateException once we try to access the EventLoop of the Channel.
promise.setFailure(cause);
} else {
// Registration was successful, so set the correct executor to use.
// See https://github.com/netty/netty/issues/2586
promise.registered();
doBind0(regFuture, channel, localAddress, promise);
}
}
});
return promise;
}
}
看这里方法面的两个核心方法:initAndRegister()
,以及doBind0(),先看initAndRegister():
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel();
init(channel);
} catch (Throwable t) {}
ChannelFuture regFuture = config().group().register(channel);
return regFuture;
}
initAndRegister主要做三个事情:new channel、init channel、register channel
newChannel():
channelFactory是
public B channel(Class extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory(channelClass));
}
这个方法是:bootstrap.channel(NioServerSocketChannel.class),即channelFactory.newChannel()最终创建channel相当于调用默认构造函数new出一个 NioServerSocketChannel对象。NioServerSocketChannel的初始化还会涉及到newChannelPipeline等各种参数的初始化,后面再单独分析。init(channel):由于是服务端的channel的init,具体方法实现我们找到ServerBootstrap.init
public class ServerBootstrap extends AbstractBootstrap {
@Override
void init(Channel channel) throws Exception {
final Map, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
final Map, Object> attrs = attrs0();
synchronized (attrs) {
for (Entry, Object> e: attrs.entrySet()) {
@SuppressWarnings("unchecked")
AttributeKey
重点看最后的p.addLast()向serverChannel的流水线处理器中加入了一个 ServerBootstrapAcceptor
,从名字上就可以看出来,这是一个接入器,专门接受新请求,把新的请求扔给某个事件循环器。
register channel:
这个ChannelFuture regFuture = config().group().register(channel)实际调用的是
NioEventLoop 中的register(实际是其父类SingleThreadEventLoop的对应方法): public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {
@Override
public ChannelFuture register(Channel channel) {
return register(new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
}
这个promise.channel().unsafe().register(this, promise)实际是:AbstractUnsafe.register,后面代码没法理解了,后面再学吧
服务端 Netty Reactor 工作架构图
Server 端包含 1 个 Boss NioEventLoopGroup 和 1 个 Worker NioEventLoopGroup。
NioEventLoopGroup 相当于 1 个事件循环组,这个组里包含多个事件循环 NioEventLoop,每个 NioEventLoop 包含 1 个 Selector 和 1 个事件循环线程。
每个 Boss NioEventLoop 循环执行的任务包含 3 步:
每个 Worker NioEventLoop 循环执行的任务包含 3 步: