参考资料
启动作用
1.和普通我们写JKD Nio的程序是一样,只是做了组件上的封装
2.首先是创建NioServerSocketChannel,注册到对应的NioEventLoop上
,启动一个线程,扫描NioEventLoop的Selector的连接事件
3.然后将NioServerSocketChannel绑定到对应端口
示例
//创建两个EventLoopGroup,一个用于连接处理,一个用于IO读写事件处理
EventLoopGroup bossEventLoopGroup = new NioEventLoopGroup(2);
EventLoopGroup workEventExecutors = new NioEventLoopGroup(2);
//创建启动类对象
ServerBootstrap serverBootstrap=new ServerBootstrap();
try {
//设置EventLoopGroup
serverBootstrap.group(bossEventLoopGroup, workEventExecutors)
//设置服务端使用的Channel,不同协议服务端使用不同的通道
.channel(NioServerSocketChannel.class)
//绑定端口
.localAddress(port)
//设置子channel使用的处理器
//处理连接的通道叫父通道,处理传输的通道叫子通道
//相应的处理器也叫父处理器和子处理器,这里是childHandler
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new MyHandler());
}
});
//所有参数都构建完毕,开始绑定,真正启动服务,也是我们分析的入口bind方法
ChannelFuture sync = serverBootstrap.bind().sync();
ChannelFuture channelFuture = sync.channel().closeFuture();
channelFuture.sync();
System.out.println("服务启动成功");
}catch (Exception e){
bossEventLoopGroup.shutdownGracefully();
workEventExecutors.shutdownGracefully();
}
ServerBootstrap的属性简单介绍
//ServerBootstrap关键属性
//处理IO事件的事件循环组
private volatile EventLoopGroup childGroup;
处理IO事件的处理器的辅助器
private volatile ChannelHandler childHandler;
//ServerBootstrap父类
//处理连接事件的事件循环组
volatile EventLoopGroup group;
//用于反射创建Channel的工厂
private volatile ChannelFactory<? extends C> channelFactory;
//处理连接事件的处理器的辅助器
private volatile ChannelHandler handler;
bind方法分析
//在bind直接调用的方法
private ChannelFuture doBind(final SocketAddress localAddress) {
//启动服务主流程
//1.做些初始化工作
//2.创建channel,将channel注册到NioEventLoop上去,开启NioEventLoop的轮训(启动线程)
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
//非主流程代码省去
.....
//绑定到对应的端口,这里操作简单,直接使用jdk底层绑定操作
doBind0(regFuture, channel, localAddress, promise);
}
initAndRegister方法
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
//使用channelFactory反射创建通道,使用初始化的NioServerSocketChannel.class
channel = channelFactory.newChannel();
//初始化通道
init(channel);
} catch (Throwable t) {
//省去....
}
//1.将通道注册到NioEventLoopGroup中的某个NioEventLoop
//2.启动NioEventLoop绑定的线程,开始轮训监听连接事件
//3.注意这里是group,而不是childGroup的某个NioEventLoop
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
newChannel方法
//构建ServerBootstrap的channel的时候,初始化了这个工厂
channel = channelFactory.newChannel();
//ServerBootstrap类
//构建ServerBootstrap的channel代码
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
//使用class属性构建的一个工厂
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
//ReflectiveChannelFactory类
//工厂使用class反射创建通道
@Override
public T newChannel() {
try {
return clazz.getConstructor().newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
init(channel)方法
//初始化通道一些信息,options(通道选项参数),ChannelPipeline(通道处理器链)
void init(Channel channel) throws Exception {
//将构建ServerBootstrap给的option参数赋值给channel
final Map<ChannelOption<?>, Object> options = options0();
synchronized (options) {
setChannelOptions(channel, options, logger);
}
//省去不关心代码...
//获取到channel的处理器流,在实例化channel的时候,构造给了DefaultChannelPipeline
//ChannelPipeline是执行处理器链,专门用来处理IO事件的,这里先不关心
//我们只需要知道,一个channel会有一个ChannelPipeline,ChannelPipeline上绑定了
//一些处理器就好了
ChannelPipeline p = channel.pipeline();
//省去不关心代码...
//先将包装好的ChannelHandle存起来,后面使用
//为ChannelPipeline绑定ChannelHandle
//这里只是绑定了ChannelInitializer,这是个特殊的ChannelHandlC
//ChannelInitializer是真正的ChannelHandle的包装,里面放了我们自己构建ServerBootstrap
//时候,真正的ChannelHandle,后面启动服务有一个动作,就是解析出我们自己添加的ChannelHandle
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
//config持有ServerBootstrap引用,里面有我们传的handler
//这里handle是父通道的ChannelHandle,一般我们不设置,但是有助于我们了解
//子通道的ChannelHandle,原理是一样的
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
}
//父通道一般是使用ServerBootstrapAcceptor,也就是Reactor的Acceptor
//我们一般不需要设置父通道的ChannelHandle的原因,这里会给默认加上
//因为父通道处理器做的事情是固定的,一般不需要我们自定义,不像子通道的处理器一样
//需要自定义我们自己的操作
ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}
register(channel)
//将通道注册到NioEventLoopGroup的一个NioEventLoop(Reactor)上的Selector上去
ChannelFuture regFuture = config().group().register(channel)
//MultithreadEventLoopGroup类
@Override
public ChannelFuture register(Channel channel) {
//选择其中一个NioEventLoop,注册上去
return next().register(channel);
}
//MultithreadEventExecutorGroup类
//调用PowerOfTwoEventExecutorChooser或者GenericEventExecutorChooser获取一个NioEventLoop
@Override
public EventExecutor next() {
return chooser.next();
}
//SingleThreadEventLoop类
//下面是NioEventLoop的register方法
@Override
public ChannelFuture register(Channel channel) {
//1.将channel包装为一个DefaultChannelPromise对象
//2.后续直接使用DefaultChannelPromise,这样可以获取channel,也能获得channel所在的
//NioEventLoop的相关信息
return register(new DefaultChannelPromise(channel, this));
}
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
//1.获取通道channel,然后注册到相应的this(NioEventLoop)上去
//2.unsafe对象是构建channel时候创建的,unsafe主要作用是包装JDK的NIO操作
promise.channel().unsafe().register(this, promise);
return promise;
}
//AbstractUnsafe类
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
//省去不需要代码..
AbstractChannel.this.eventLoop = eventLoop;
//判断当前NioEventLoop的轮训线程是否启动,分两种情况
//1.启动过,那么直接注册
//2.没启动过,先启动线程,然后注册register0
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
//启动NioEventLoop的线程,注册任务传递
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
}
NioEventLoop的execute方法
//SingleThreadEventExecutor类(父类)
//启动NioEventLoop线程
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
boolean inEventLoop = inEventLoop();
//是否启动过线程两种情况
//1.启动过,那么直接将任务添加到队列
//2.没启动过,那么启动一个线程,并且将任务添加到队列中
if (inEventLoop) {
addTask(task);
} else {
//启动线程
startThread();
addTask(task);
if (isShutdown() && removeTask(task)) {
reject();
}
}
}
//启动线程
private void startThread() {
if (state == ST_NOT_STARTED) {
if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
doStartThread();
}
}
}
//使用线程工具executor启动一个线程
private void doStartThread() {
assert thread == null;
executor.execute(new Runnable() {
@Override
public void run() {
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
//省去.....
//执行轮训方法
SingleThreadEventExecutor.this.run();
//省去.....
}
}
}
//轮训方法,这里会获取队列任务进行执行
//1.比如回调上面的register0(promise);,会取出任务,然后将channel注册到selector上
//2.轮训获取连接任务,然后使用连接处理器连接
@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();
}
// fall through
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);
}
}
}
总结
1.本节主要是探索服务端怎么运行起来的,怎么绑定一个NioEventLoop,然后工作起来的一个流程
2.下面章节还需要分析轮训里面具体做了一些什么,channel,NioEventLoop,handle,连接处理器接受连接等的细节