暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Netty源码分析篇(二):BootStrap的启动

没意思先生 2020-02-10
575
首先我们观察一段Netty关于BootStrap启动的代码:
//创建Boostrap对象
Bootstrap bootstrap = new Bootstrap();
//创建线程组,处理消息读取
EventLoopGroup group = new NioEventLoopGroup();
bootstrap.group(group) //设置使用的eventLoopGroup
.channel(NioSocketChannel.class) //设置被实例化的NioServerSocketChannel类
.handler(new ChannelInitializer<NioSocketChannel>() { // 设置SocketChannel处理器
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// TODO
}
});
try {
bootstrap.connect("127.0.0.1",2237).sync().channel().closeFuture().sync();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
这是一段Netty客户端启动的代码,我们先是创建了EventLoopGroup,其实就是线程池,然后我们指定了服务端channel类型是NioSocketChannel.class,以NIO的方式进行处理,然后我们指定了处理业务逻辑的NioSocketChannel的处理器,紧接着是连接,然后进行阻塞(这里阻塞一般是因为后续没有执行代码,线程退出,原来创建的对象也关闭回收,如果线程没有停止的话,可以不阻塞).
然后我们看看Java原生NIO是怎么写的:
// 调用Selector.open()方法创建一个Selector对象
Selector selector = Selector.open();
// 通过调用SocketChannel.open()方法来打开SocketChannel
SocketChannel sc = SocketChannel.open();
// 设置channel非阻塞
sc.configureBlocking(false);
if (!sc.connect(new InetSocketAddress("127.0.0.1", 2237))){
//不断地轮询连接状态,直到完成连接
while (!sc.finishConnect()){
// TODO
}
}
// 设置selector关注channel的READ请求事件和WRITE事件
sc.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);

Java原生NIO方式一如既往的简单,当然Netty也是做了一些好的封装操作,比如单线程封装处理等.因为Boostrap的启动较为简单,我们就分析在哪实现了Java NIO的封装即可,首先是在哪实现了connect()方法,由时序图可知道在NioSocketChannel处的doConnect()方法实现:

NioSocketChannel处的doConnect()方法代码如下:

protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
// 略
boolean success = false;
try {
boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);
// 略
success = true;
return connected;
} finally {
if (!success) {
doClose();
}
}
}

SocketUtils的connect()方法代码如下:

public static void connect(final Socket socket, final SocketAddress remoteAddress, final int timeout)
throws IOException {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws IOException {
socket.connect(remoteAddress, timeout);
return null;
}
});
} catch (PrivilegedActionException e) {
throw (IOException) e.getCause();
}
}

最终也是调用了Java Nio的API进行connect远程IP和端口.然后和前文(Netty源码分析篇(一):ServerBootStrap的启动)提及一样通过pipeline触发channelActive()方法到Selector中设置感兴趣的事件.最终设置事件在AbstractNioChannel的doBeginRead()方法内实现,具体代码如下:

@Override
protected void doBeginRead() throws Exception {
// 略
final int interestOps = selectionKey.interestOps();
if ((interestOps & readInterestOp) == 0) {
// 将之前 Channel 注册到 EventLoop 上的 Selector 时产生的 SelectionKey 的关注事件集合进行更新
// NioServerSocketChannel 的传入的值为 OP_ACCEPT,也就是其感兴趣的事件为客户端接入就绪事件
// NioSocketChannel 的传入的值为 OP_READ,也就是其感兴趣的事件为读就绪事件
selectionKey.interestOps(interestOps | readInterestOp);
}
}

方法内的readInterestOp是在构造参数就写好的,NioSocketChannel继承自AbstractNioByteChannel,传入的是SelectionKey.OP_READ,因为NioSocketChannel会接收到Selector的读事件的通知.

整个Bootstrap启动流程的时序图如下:

时序图链接:

https://www.processon.com/view/link/5e40e2f8e4b085b5f21a7193

文章转载自没意思先生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论