NioEventLoopGroup group1= new NioEventLoopGroup();
NioEventLoopGroup group2 = new NioEventLoopGroup();
try{
ServerBootstrap boot = new ServerBootstrap();
boot.group(group1,group2)
.channel(NioSeverSocketChannel.class)
.localAddress(new InetAddress(port))
.childHanlder(new ChannelInitializer<Channel>(){
@Override
public void initChannel(Channel ch){
ch.pipeline().addLast(handler);
}
});
ChannelFuture future = boot.bind().sync();
future.channel().closeFuture().sync();
}catch(){
}finaly{
group1.shutdownGracefully();
group2.shutdowngracefully();
}
以上代码是程序启动的简易代码。
对于Nio模型的理解。首先是将要关注的option 和Channel注册给操作系统内核selector。根据option 的值做出对应的操作。
SelectionKey.OP_ACCEPT =1<<4;
SelectionKey.OP_CONNECt =1<<3;
SelectionKey.OP_READ =1<<0;
SelectionKey.OP_WRITE =1<<2;
NIoEventLoopGroup : 里面包含了一个EventExecutor[ i ] 的线程数组,里面的线程是NioEventLoop
NioEventLoop 里面包含了Selector, SelectionKey Set<SelectionKey> 主要功能是channel绑定该类,该类开启对应的selectNow(),进行轮训操作。因此该类包含有一个Executor 该线程将会执行一个任务,执行 该类中run 方法,进行轮训。
NioServerSocketChannel 该父类中会包含一个内部类Unsafe Unsafe 类将会进行对应的register操作和bind操作。将这些操作进行了封装,具体方法参考对应的APi 或者源码。
DefaultChannelPipeline:作为一个职责链。内部有一个链表 DefaultChannelHandlerContext 内部类: HeadContext TailContext PendingHandlerCallback... callback 作为了为handler添加删除创建的一个任务,会在相应的方法中进行回调实现对应的功能。
DefaultChannelHandlerContext:将handler包含在其中,作为handler 使用的文本切换。其实就是使用该链表的iteration进行切换而已。具体的方法会有下一个context方法的回调。
在boot.bind() 方法执行前首先我们创建了两个线程池。group1 和group2 group1 作为启动的注册和绑定的池子,group2 作为IO事件操作的池子。
在对NioEventLoopGroup 进行初始化的时候,我们使用了一个无参构造函数。其实它会调用该类的其他构造函数。可以思考的是,我们需要参数,数组的大小int 在NioEventExecutor 中的执行线程,线程工厂,Selector的构造器,执行option操作的策略模式工厂,rejectionhandler
初始化的时候,也会创建新的NioEventLoop并对其进行初始话。放入到Executor[ ] 数组中,对于NioEventGroup会添加对应的Listeners
ServerBootstrap 通过build方式创建类的初始化构造。
boot.bind() 在源码中我们会看到有两步操作。实际代码我会将他看成3步操作。
1:init
2:register
3:bind
1---第一步初始化的时候,是对channel做初始化操作,具体可以看ServerBootstrap。init(channel) 方法下的内容,值得注意的是在创建NioServerSocketChannel的时候,是通过反射的方式创建其本身类。具体的factory 是ReflectChannelFactory 创建,其中在创建该类时候,会对应的创建DefaulChannelPipeline 而DefaultChannelPipeline会创建HeadContext TailContext 构建初始化的链表。具体字段的初始化,不详细介绍,详细去看构造详情。值得注意的是在对DefaultChannelPipeline 初始化后,会添加一个ChannelInitializer<Channel> handler 该handler添加细节详细去看DefaultChannelPipeline 中的APi,其操作无非是因为还未注册eventLoop到channel上,会将其添加到ChannelPipeline 中PendingHandlerCallback 任务中,会在后面register 操作的时候回调:pipeline.invokeHandlerAddedIfneed() 调用该handler的初始化方法。;
2-----第二步的操作在AbstractBootstrap 下进行的 confg().group().register(channel);该register的方法调用流程详细看源码操作。其中最重要的一个点是在eventloop.inEventLoop() 做判断的时候,要看下源码中关于execute()方法是如何执行的,在AbstractChannel,register(executor,promise)时候,是一个单线程,如果在main线程中,此时,返回的是false。会执行添加一个任务到NioEventLoop中的taskQueue中。需要注意的是,并没有立即执行,但是会调用执行NioEventLoop下的线程executor 会execute(task)该任务会创建Thread调用NioEventLoop下的run() 方法 也就是会根据策略实现是否开始selector()轮询的操作。 其中taskQueue 中的任务会根据该run()方法下策略情况调用该任务中的run()方法,而并不是新建线程执行该类的方法。因此在后面的代码中关于eventLoop().execute(Runnable task) 时候,要注意并没有开启一个线程。因此到目前为止 也就是开启了两个线程。main 线程和 group1 命名的线程1。最终的register 就是根据NioServerSocketChannel下的Unsafe类进行了具体的注册。其实质本身是使用java APi 下的ServerSocketChannel.register()方法。
3----第三步的操作是对应的bind(SocketAddress) 此时也是将会新建一个任务将其添加到taskQueue中,在线程循环for(;;)中根据情况调用run()方法。在该线程中对NioEventLoop 下的IntSupplier 下的方法调用,主要是开始selectNow()方法调用时候,由于已经注册和绑定 ,会在循环的操作下进行轮询 操作,根据option的策略,进行事件的传播,进行handler传递操作。
当然其中read和write操作主要是通过Accepter 来完成这样的操作。具体详细的情况查看APi,或后续待介绍。
文字并不多,但是需要查看的类和方法是非常多,以及逻辑是根据反复查看源码得到的对自己精简的心得。
源码中有些操作是值得学习的。类的config类,是对相互的引用。如何通过修改config改变其中的内容,另外在其他的类中也反复涉及到这种使用。当然源码中也多次对回调函数的使用。逻辑上要非常的谨慎。
原文:https://www.cnblogs.com/futureT/p/13601061.html