Netty源码—1.服务端启动流程二
大纲
1.服务端启动整体流程及关键方法
2.服务端启动的核心步骤
3.创建服务端Channel的源码
4.初始化服务端Channel的源码
5.注册服务端Channel的源码
6.绑定服务端端口的源码
7.服务端启动流程源码总结
5.注册服务端Channel的源码
(1)注册服务端Channel的入口
(2)注册Selector的主要步骤
(3)注册服务端Channel总结
(1)注册服务端Channel的入口
首先AbstractBootstrap的config()方法是一个抽象方法,会由ServerBootstrap来实现。
ServerBootstrap的config()方法会返回一个封装了ServerBootstrap对象的ServerBootstrapConfig对象。所以执行代码config().group()时会调用AbstractBootstrapConfig的group()方法,也就是执行ServerBootstrap的group()方法返回用户通过group()方法设置的一个NioEventLoopGroup对象。因此config().group().register(channel)最后会调用NioEventLoopGroup的register()方法。
//AbstractBootstrap is a helper class that makes it easy to bootstrap a Channel.
//It support method-chaining to provide an easy way to configure the AbstractBootstrap.
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {volatile EventLoopGroup group;...final ChannelFuture initAndRegister() {Channel channel = null;...//1.创建服务端Channelchannel = channelFactory.newChannel();//2.初始化服务端Channelinit(channel);...//3.注册服务端Channel并启动一个NioEventLoop线程,通过NioEventLoopGroup的register()方法进行注册ChannelFuture regFuture = config().group().register(channel);...return regFuture;}//Returns the AbstractBootstrapConfig object that can be used to obtain the current config of the bootstrap.public abstract AbstractBootstrapConfig<B, C> config();//Returns the configured EventLoopGroup or null if non is configured yet.public final EventLoopGroup group() {return group;}...
}//Bootstrap sub-class which allows easy bootstrap of ServerChannel
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);...@Overridepublic final ServerBootstrapConfig config() {return config;}...
}public abstract class AbstractBootstrapConfig<B extends AbstractBootstrap<B, C>, C extends Channel> {protected final B bootstrap;...protected AbstractBootstrapConfig(B bootstrap) {this.bootstrap = ObjectUtil.checkNotNull(bootstrap, "bootstrap");}//Returns the configured EventLoopGroup or null if non is configured yet.public final EventLoopGroup group() {//比如返回一个NioEventLoopGroup对象return bootstrap.group();}...
}
NioEventLoopGroup继承自抽象类MultithreadEventLoopGroup,调用NioEventLoopGroup的register()方法也就是调用MultithreadEventLoopGroup的register()方法。
调用NioEventLoopGroup的register()方法时,会先通过next()方法获取一个NioEventLoop对象,然后再调用NioEventLoop的register()方法。而调用NioEventLoop的register()方法,其实就是调用抽象类SingleThreadEventLoop的register()方法。
在SingleThreadEventLoop的register()方法中,promise.channel().unsafe()会返回一个Channel.Unsafe类型的对象。而AbstractChannel实现了Channel接口,AbstractChannel的内部类AbstractUnsafe也实现了Channel接口的内部接口Unsafe。
所以promise.channel().unsafe().register(this, promise)最后会调用AbstractUnsafe的register()方法。
//MultithreadEventLoopGroup implementations which is used for NIO Selector based Channels.
public class NioEventLoopGroup extends MultithreadEventLoopGroup {......
}//Abstract base class for EventLoopGroup implementations that handles their tasks with multiple threads at the same time.
public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {...@Overridepublic ChannelFuture register(Channel channel) {//先通过next()方法获取一个NioEventLoop,然后通过NioEventLoop.register()方法注册服务端Channelreturn next().register(channel);}@Overridepublic EventLoop next() {return (EventLoop) super.next();}...
}//SingleThreadEventLoop implementation which register the Channel's to a Selector and so does the multi-plexing of these in the event loop.
public final class NioEventLoop extends SingleThreadEventLoop {......
}//Abstract base class for EventLoops that execute all its submitted tasks in a single thread.
public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {...@Overridepublic ChannelFuture register(Channel channel) {return register(new DefaultChannelPromise(channel, this));}@Overridepublic ChannelFuture register(final ChannelPromise promise) {ObjectUtil.checkNotNull(promise, "promise");//调用AbstractUnsafe的register()方法promise.channel().unsafe().register(this, promise);return promise;}...
}
所以注册服务端Channel的关键逻辑其实就体现在AbstractUnsafe的register()方法上。该方法会先将EventLoop事件循环器绑定到服务端Channel即NioServerSocketChanel上,然后再调用AbstractUnsafe的register0()方法将服务端Channel注册到Selector上。
//A skeletal Channel implementation.
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {private volatile EventLoop eventLoop;...//Unsafe implementation which sub-classes must extend and use.protected abstract class AbstractUnsafe implements Unsafe {...@Overridepublic final void register(EventLoop eventLoop, final ChannelPromise promise) {...//绑定事件循环器,即绑定一个NioEventLoop到该Channel上AbstractChannel.this.eventLoop = eventLoop;//注册Selector,并启动一个NioEventLoopif (eventLoop.inEventLoop()) {register0(promise);} else {...//通过启动这个NioEventLoop线程来调用register0()方法将这个服务端Channel注册到Selector上eventLoop.execute(new Runnable() {@Overridepublic void run() {register0(promise);}});...}}private void register0(ChannelPromise promise) {...}...}...
}
注意:AbstractUnsafe的register()方法会将前面获取到的一个NioEventLoop事件循环器绑定到服务端Channel上,之后便可以通过channel.eventLoop()来取出这个NioEventLoop事件循环器了。因此,一个服务端Channel对应一个NioEventLoop事件循环器。此外,会通过启动一个NioEventLoop线程来调用register0()方法将服务端Channel注册到Selector上。
总结:创建服务端Channel后,就会从NioEventLoopGroup中获取一个NioEventLoop出来进行绑定,并启动这个NioEventLoop线程将这个服务端Channel注册到Selector上以及执行线程的run()逻辑监听事件等。
(2)注册Selector的主要步骤
AbstractUnsafe.register0()方法主要有4个步骤。
步骤一:调用JDK底层注册服务端Channel到Selector上
doRegister()方法是由AbstractChannel的子类AbstractNioChannel来实现的。
在AbstractNioChannel的doRegister()方法中,首先获取前面创建的JDK底层NIO的Channel,然后调用JDK底层NIO的register()方法,将this也就是NioServerSocketChannel对象当作attachment绑定到JDK的Selector上。这样绑定是为了后续从Selector拿到对应的事件后,可以把Netty领域的Channel拿出来。而且注册的ops值是0,表示此时还不关注任何事件。
步骤二:回调handlerAdded事件
步骤三:传播channelRegisterd事件
步骤四:其他逻辑
//A skeletal Channel implementation.
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {private volatile EventLoop eventLoop;...//Unsafe implementation which sub-classes must extend and use.protected abstract class AbstractUnsafe implements Unsafe {...@Overridepublic final void register(EventLoop eventLoop, final ChannelPromise promise) {...//绑定事件循环器,即绑定一个NioEventLoop到该Channel上AbstractChannel.this.eventLoop = eventLoop;//注册Selector,并启动一个NioEventLoopif (eventLoop.inEventLoop()) {register0(promise);} else {...//通过启动这个NioEventLoop线程来调用register0()方法将这个服务端Channel注册到Selector上eventLoop.execute(new Runnable() {@Overridepublic void run() {register0(promise);}});...}}private void register0(ChannelPromise promise) {...boolean firstRegistration = this.neverRegistered;//1.调用JDK底层注册服务端Channel到Selector上doRegister();this.neverRegistered = false;this.registered = true;//2.回调handlerAdded事件this.pipeline.invokeHandlerAddedIfNeeded();safeSetSuccess(promise);//3.传播channelRegisterd事件到用户代码里this.pipeline.fireChannelRegistered();//4.其他逻辑if (isActive()) {if (firstRegistration) {this.pipeline.fireChannelActive();} else if (config().isAutoRead()) {beginRead();}}...}...}//Is called after the Channel is registered with its EventLoop as part of the register process.//Sub-classes may override this methodprotected void doRegister() throws Exception {// NOOP}...
}//Abstract base class for Channel implementations which use a Selector based approach.
public abstract class AbstractNioChannel extends AbstractChannel {private final SelectableChannel ch;//这是NIO中的Channelprotected final int readInterestOp;volatile SelectionKey selectionKey;...//Create a new instance//@param parent,the parent Channel by which this instance was created. May be null.//@param ch,he underlying SelectableChannel on which it operates//@param readInterestOp,the ops to set to receive data from the SelectableChannelprotected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {super(parent);//NioServerSocketChannel.newSocket()方法通过JDK底层创建的Channel对象会被缓存在其父类AbstractNioChannel的变量ch中//可以通过NioServerSocketChannel.javaChannel()方法获取其父类AbstractNioChannel的变量chthis.ch = ch;this.readInterestOp = readInterestOp;...//设置Channel对象为非阻塞模式ch.configureBlocking(false);...}@Overrideprotected void doRegister() throws Exception {boolean selected = false;for (;;) {...//首先获取前面创建的JDK底层NIO的Channel,然后调用JDK底层NIO的register()方法,//将this也就是NioServerSocketChannel对象当作attachment绑定到JDK的Selector上;//这样绑定是为了后续从Selector拿到对应的事件后,可以把Netty领域的Channel拿出来;//而且注册的ops值是0,表示此时还不关注任何事件;selectionKey = javaChannel().register(eventLoop().selector, 0, this);return;...}}protected SelectableChannel javaChannel() {return ch;}...
}
(3)注册服务端Channel总结
注册服务端Channel的入口是AbstractChannel的内部类AbstractUnsafe的register()方法。
首先会把一个NioEventLoop线程和当前的Channel进行绑定,然后再调用AbstractUnsafe的register0()方法进行注册。而register0()方法会把前面创建的JDK底层NIO的Channel注册到Selector上,并且把Netty领域的Channel当作一个attachment绑定到Selector上去,最后回调handlerAdded事件以及传播channelRegistered事件到用户代码里。
ServerBootstrap.bind() //用户代码入口AbstractBootstrap.initAndRegister() //初始化并注册ChannelchannelFactory.newChannel() //创建服务端ChannelServerBootstrap.init() //初始化服务端ChannelNioEventLoopGroup.register() //注册服务端ChannelNioEventLoop.register() //注册服务端ChannelAbstractChannel.AbstractUnsafe.register() //注册Channel入口this.eventLoop = eventLoop //将Channel绑定NioEventLoop线程AbstractChannel.AbstractUnsafe.register0() //实际注册AbstractNioChannel.doRegister() //调用JDK底层注册Channel到SelectorinvokeHandlerAddedIfNeeded() //回调handlerAdded事件fireChannelRegistered() //传播channelRegistered事件
补充说明一:Java类是单继承的,Java接口却是多继承的。因为前者不能区分父类相同名字方法要用哪一个,后者则由于还没实现接口,即使父类有相同名字接口也不影响。
public interface EventLoop extends OrderedEventExecutor, EventLoopGroup {...
}public abstract class SingleThreadEventLoop extends SingleThreadEventExecutor implements EventLoop {...
}
补充说明二:如果监听一个端口,就创建一个服务端Channel。如果监听多个端口,就创建多个服务端Channel。
每个Channel绑定于NioEventLoopGroup的next()方法返回的一个NioEventLoop。
6.绑定服务端端口的源码
(1)绑定服务端端口的时机
(2)AbstractUnsafe.bind()方法的主要工作
(3)调用JDK底层绑定端口
(4)传播ChannelActive事件
(5)注册ACCEPT事件到Selector
(6)绑定服务端端口总结
(1)绑定服务端端口的时机
ServerBootstrap的bind()方法,首先执行AbstractBootstrap的initAndRegister()方法完成了服务端Channel的初始化和注册后,就会调用AbstractBootstrap的doBind0()方法绑定端口。
//Bootstrap sub-class which allows easy bootstrap of ServerChannel
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {......
}//AbstractBootstrap is a helper class that makes it easy to bootstrap a Channel.
//It support method-chaining to provide an easy way to configure the AbstractBootstrap.
//When not used in a ServerBootstrap context, the #bind() methods are useful for connectionless transports such as datagram (UDP).
public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {...//Create a new Channel and bind it.public ChannelFuture bind(int inetPort) {//首先根据端口号创建一个InetSocketAddress对象,然后调用重载方法bind()return bind(new InetSocketAddress(inetPort));}//Create a new Channel and bind it.public ChannelFuture bind(SocketAddress localAddress) {//验证服务启动需要的必要参数validate();if (localAddress == null) throw new NullPointerException("localAddress");return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));}private ChannelFuture doBind(final SocketAddress localAddress) {final ChannelFuture regFuture = initAndRegister();//1.初始化和注册Channelfinal Channel channel = regFuture.channel();...doBind0(regFuture, channel, localAddress, promise);//2.绑定服务端端口...return promise;}private static void doBind0(final ChannelFuture regFuture, final Channel channel,final SocketAddress localAddress, final ChannelPromise promise) {//This method is invoked before channelRegistered() is triggered.//Give user handlers a chance to set up the pipeline in its channelRegistered() implementation.channel.eventLoop().execute(new Runnable() {@Overridepublic void run() {if (regFuture.isSuccess()) {channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);} else {promise.setFailure(regFuture.cause());}}});}...
}
(2)AbstractUnsafe.bind()方法的主要工作
AbstractBootstrap的doBind0()方法会执行代码channel.bind(),这个channel其实就是通过channelFactory工厂反射生成的NioServerSocketChannel。
所以执行channel.bind()其实就是执行AbstractChannel的bind()方法。经过逐层调用,最后会落到调用AbstractChannel内部类AbstractUnsafe的bind()方法。
AbstractUnsafe的bind()方法主要做两件事:
一.调用JDK底层绑定端口
二.传播channelActive事件并注册ACCEPT事件
//A ServerSocketChannel implementation which uses NIO selector based implementation to accept new connections.
public class NioServerSocketChannel extends AbstractNioMessageChannel implements ServerSocketChannel {...
}//AbstractNioChannel base class for Channels that operate on messages.
public abstract class AbstractNioMessageChannel extends AbstractNioChannel {...
}//Abstract base class for Channel implementations which use a Selector based approach.
public abstract class AbstractNioChannel extends AbstractChannel {...
}//A skeletal {@link Channel} implementation.
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {...private final DefaultChannelPipeline pipeline;@Overridepublic ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {return pipeline.bind(localAddress, promise);}...
}//The default ChannelPipeline implementation.
//It is usually created by a Channel implementation when the Channel is created.
public class DefaultChannelPipeline implements ChannelPipeline {final AbstractChannelHandlerContext head;final AbstractChannelHandlerContext tail;...@Overridepublic final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {return tail.bind(localAddress, promise);}...
}abstract class AbstractChannelHandlerContext extends DefaultAttributeMap implements ChannelHandlerContext, ResourceLeakHint {...@Overridepublic ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {if (localAddress == null) throw new NullPointerException("localAddress");if (!validatePromise(promise, false)) return promise;final AbstractChannelHandlerContext next = findContextOutbound();EventExecutor executor = next.executor();if (executor.inEventLoop()) {next.invokeBind(localAddress, promise);} else {safeExecute(executor, new Runnable() {@Overridepublic void run() {next.invokeBind(localAddress, promise);}}, promise, null);}return promise;}private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {if (invokeHandler()) {try {//执行DefaultChannelPipeline.HeadContext的bind()方法((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);} catch (Throwable t) {notifyOutboundHandlerException(t, promise);}} else {bind(localAddress, promise);}}...
}//The default ChannelPipeline implementation.
//It is usually created by a Channel implementation when the Channel is created.
public class DefaultChannelPipeline implements ChannelPipeline {...final class HeadContext extends AbstractChannelHandlerContext implements ChannelOutboundHandler, ChannelInboundHandler {private final Unsafe unsafe;HeadContext(DefaultChannelPipeline pipeline) {super(pipeline, null, HEAD_NAME, false, true);unsafe = pipeline.channel().unsafe();setAddComplete();}...@Overridepublic void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {//执行AbstractChannel内部类AbstractUnsafe的bind()方法unsafe.bind(localAddress, promise);}...}...
}//A skeletal {@link Channel} implementation.
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {private final DefaultChannelPipeline pipeline;...//Unsafe implementation which sub-classes must extend and use.protected abstract class AbstractUnsafe implements Unsafe {...@Overridepublic final void bind(final SocketAddress localAddress, final ChannelPromise promise) {...boolean wasActive = isActive();try {//1.调用JDK底层绑定端口doBind(localAddress);} catch (Throwable t) {safeSetFailure(promise, t);closeIfClosed();return;}if (!wasActive && isActive()) {invokeLater(new Runnable() {@Overridepublic void run() {//2.传播channelActive事件并注册ACCEPT事件pipeline.fireChannelActive();}});}safeSetSuccess(promise);}...}...//Bind the Channel to the SocketAddressprotected abstract void doBind(SocketAddress localAddress) throws Exception;...
}
(3)调用JDK底层绑定端口
AbstractUnsafe的bind()方法中所调用的doBind()方法是属于AbstractChannel的抽象接口,会由NioServerSocketChannel来进行具体的实现,即调用JDK底层NIO的bind()方法来绑定端口。
//A skeletal {@link Channel} implementation.
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {...//Bind the Channel to the SocketAddressprotected abstract void doBind(SocketAddress localAddress) throws Exception;...
}//A ServerSocketChannel implementation which uses NIO selector based implementation to accept new connections.
public class NioServerSocketChannel extends AbstractNioMessageChannel implements ServerSocketChannel {...@Overrideprotected void doBind(SocketAddress localAddress) throws Exception {if (PlatformDependent.javaVersion() >= 7) {javaChannel().bind(localAddress, config.getBacklog());} else {javaChannel().socket().bind(localAddress, config.getBacklog());}}@Overrideprotected ServerSocketChannel javaChannel() {return (ServerSocketChannel) super.javaChannel();}...
}//Abstract base class for Channel implementations which use a Selector based approach.
public abstract class AbstractNioChannel extends AbstractChannel {private final SelectableChannel ch;//这是NIO中的Channel...protected SelectableChannel javaChannel() {return ch;}...
}
(4)传播ChannelActive事件
绑定完端口后,就会执行代码pipeline.fireChannelActive(),也就是调用DefaultChannelPipeline.fireChannelActive()。
最后会调用DefaultChannelPipeline.HeadContext的channelActive()方法传播channelActive事件。
//The default ChannelPipeline implementation.
//It is usually created by a Channel implementation when the Channel is created.
public class DefaultChannelPipeline implements ChannelPipeline {final AbstractChannelHandlerContext head;final AbstractChannelHandlerContext tail;...@Overridepublic final ChannelPipeline fireChannelActive() {AbstractChannelHandlerContext.invokeChannelActive(head);return this;}...
}abstract class AbstractChannelHandlerContext extends DefaultAttributeMap implements ChannelHandlerContext, ResourceLeakHint {...static void invokeChannelActive(final AbstractChannelHandlerContext next) {EventExecutor executor = next.executor();if (executor.inEventLoop()) {next.invokeChannelActive();} else {executor.execute(new Runnable() {@Overridepublic void run() {next.invokeChannelActive();}});}}private void invokeChannelActive() {if (invokeHandler()) {try {//执行DefaultChannelPipeline.HeadContext的channelActive()方法((ChannelInboundHandler) handler()).channelActive(this);} catch (Throwable t) {notifyHandlerException(t);}} else {fireChannelActive();}}
}//The default ChannelPipeline implementation.
//It is usually created by a Channel implementation when the Channel is created.
public class DefaultChannelPipeline implements ChannelPipeline {...final class HeadContext extends AbstractChannelHandlerContext implements ChannelOutboundHandler, ChannelInboundHandler {...@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {//1.传播channelActive事件ctx.fireChannelActive();//2.注册ACCEPT事件readIfIsAutoRead();}...}
}
(5)注册ACCEPT事件到Selector
传播完channelActive事件后,便会调用HeadContext.readIfIsAutoRead()方法。然后逐层调用到AbstractChannel内部类AbstractUnsafe的beginRead()方法,并最终调用到AbstractNioChannel的doBeginRead()方法来注册ACCEPT事件。
//The default ChannelPipeline implementation.
//It is usually created by a Channel implementation when the Channel is created.
public class DefaultChannelPipeline implements ChannelPipeline {private final Channel channel;...final class HeadContext extends AbstractChannelHandlerContext implements ChannelOutboundHandler, ChannelInboundHandler {...private void readIfIsAutoRead() {//isAutoRead()方法默认会返回trueif (channel.config().isAutoRead()) {//调用AbstractChannel的read()方法channel.read();}}...}
}//A skeletal {@link Channel} implementation.
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {private final DefaultChannelPipeline pipeline;...@Overridepublic Channel read() {pipeline.read();return this;}...
}//The default ChannelPipeline implementation.
//It is usually created by a Channel implementation when the Channel is created.
public class DefaultChannelPipeline implements ChannelPipeline {final AbstractChannelHandlerContext head;final AbstractChannelHandlerContext tail;...@Overridepublic final ChannelPipeline read() {tail.read();return this;}...
}abstract class AbstractChannelHandlerContext extends DefaultAttributeMap implements ChannelHandlerContext, ResourceLeakHint {...@Overridepublic ChannelHandlerContext read() {final AbstractChannelHandlerContext next = findContextOutbound();EventExecutor executor = next.executor();if (executor.inEventLoop()) {next.invokeRead();} else {Runnable task = next.invokeReadTask;if (task == null) {next.invokeReadTask = task = new Runnable() {@Overridepublic void run() {next.invokeRead();}};}executor.execute(task);}return this;}private void invokeRead() {if (invokeHandler()) {try {//执行DefaultChannelPipeline.HeadContext的read()方法((ChannelOutboundHandler) handler()).read(this);} catch (Throwable t) {notifyHandlerException(t);}} else {read();}}...
}//The default ChannelPipeline implementation.
//It is usually created by a Channel implementation when the Channel is created.
public class DefaultChannelPipeline implements ChannelPipeline {...final class HeadContext extends AbstractChannelHandlerContext implements ChannelOutboundHandler, ChannelInboundHandler {private final Unsafe unsafe;HeadContext(DefaultChannelPipeline pipeline) {super(pipeline, null, HEAD_NAME, false, true);unsafe = pipeline.channel().unsafe();setAddComplete();}...@Overridepublic void read(ChannelHandlerContext ctx) {unsafe.beginRead();}...}
}//A skeletal {@link Channel} implementation.
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {private final DefaultChannelPipeline pipeline;...//Unsafe implementation which sub-classes must extend and use.protected abstract class AbstractUnsafe implements Unsafe {...@Overridepublic final void beginRead() {assertEventLoop();if (!isActive()) return;try {doBeginRead();} catch (final Exception e) {invokeLater(new Runnable() {@Overridepublic void run() {pipeline.fireExceptionCaught(e);}});close(voidPromise());}}...}//Schedule a read operation.protected abstract void doBeginRead() throws Exception;...
}//Abstract base class for Channel implementations which use a Selector based approach.
public abstract class AbstractNioChannel extends AbstractChannel {protected final int readInterestOp;volatile SelectionKey selectionKey;boolean readPending;...@Overrideprotected void doBeginRead() throws Exception {//Channel.read() or ChannelHandlerContext.read() was called//this.selectionKey就是前面注册服务端Channel时返回的对象//注册服务端Channel时,注册ops的值是0,表示还不关注任何事件final SelectionKey selectionKey = this.selectionKey;if (!selectionKey.isValid()) return;readPending = true;final int interestOps = selectionKey.interestOps();//这里的readInterestOp就是前面newChannel()时传入的SelectionKey.OP_ACCEPT//所以这样要做的工作就是,告诉JDK的Selector一切工作准备就绪,只剩下把ACCEPT事件注册到Selector上if ((interestOps & readInterestOp) == 0) {//关注ACCEPT事件selectionKey.interestOps(interestOps | readInterestOp);}}...
}
(6)绑定服务端端口总结
绑定服务端端口,最终会调用JDK底层API去进行实际绑定。绑定端口成功后,会由DefaultChannelPipeline传播channelActive事件,以及把ACCEPT事件注册到Selector上,从而可以通过Selector监听新连接的接入。
ServerBootstrap.bind() //用户代码入口AbstractBootstrap.initAndRegister() //初始化并注册ChannelchannelFactory.newChannel() //创建服务端ChannelServerBootstrap.init() //初始化服务端ChannelNioEventLoopGroup.register() //注册服务端ChannelAbstractBootstrap.doBind0() //绑定服务端端口AbstractChannel.AbstractUnsafe.bind() //绑定服务端端口入口NioServerSocketChannel.doBind() //NioServerSocketChannel实现javaChannel().bind() //JDK底层API绑定端口DefaultChannelPipeline.fireChannelActive() //传播channelActive事件HeadContext.readIfIsAutoRead() //注册ACCEPT事件到Selector上
7.服务端启动流程源码总结
initAndRegister()里的newChannel()会通过反射创建JDK底层Channel,同时会创建该Channel对应的Config对象并设置该Channel为非阻塞模式。总之,创建服务端Channel时会完成Netty几大基本组件的创建。如Channel、ChannelConfig、ChannelId、Unsafe、ChannelPipeline。
初始化服务端Channel时,会设置服务端Channel和客户端Channel的Option和Attr,并且给服务端Channel添加连接接入器ServerBootstrapAcceptor用于接收新连接。
注册服务端Channel时,会调用JDK底层的API将Channel注册到Selector,同时将Netty领域的Channel当作attachment注册到Selector上,并且回调handlerAdded事件和传播channelRegistered事件到其他用户代码中。
绑定服务端端口时,会调用JDK底层API进行端口绑定并传播channelActive事件。当channelActive事件被传播后,才真正进行有效的服务端端口绑定,也就是把ACCEPT事件注册到Selector上。
相关文章:
Netty源码—1.服务端启动流程二
大纲 1.服务端启动整体流程及关键方法 2.服务端启动的核心步骤 3.创建服务端Channel的源码 4.初始化服务端Channel的源码 5.注册服务端Channel的源码 6.绑定服务端端口的源码 7.服务端启动流程源码总结 5.注册服务端Channel的源码 (1)注册服务端Channel的入口 (2)注册…...
Python OCR文本识别详细步骤及代码示例
光学字符识别(OCR)是将图像中的文字转换为可编辑文本的技术。在Python中,我们可以利用多种库实现OCR功能。本文将详细介绍使用Tesseract和EasyOCR进行文本识别的步骤,并提供完整的代码示例。 一、OCR简介 OCR(Optical…...
springmvc 框架学习
什么是 SpringMVC 框架 Spring MVC 是 Spring 框架的核心模块之一,基于 Java Servlet API 构建的 Web 层解决方案。它实现了 MVC 设计模式(Model-View-Controller),专为开发灵活、松耦合的 Web 应用程序而设计。 在控制层框架历…...
学习threejs,构建THREE.ParametricGeometry参数化函数生成几何体
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️THREE.ParametricGeometry1…...
【华为OD-E卷 - 单词接龙 100分(python、java、c++、js、c)】
【华为OD-E卷 - 单词接龙 100分(python、java、c、js、c)】 题目 单词接龙的规则是: 可用于接龙的单词首字母必须要前一个单词的尾字母相同; 当存在多个首字母相同的单词时,取长度最长的单词,如果长度也相…...
美团Leaf分布式ID生成器使用教程:号段模式与Snowflake模式详解
引言 在分布式系统中,生成全局唯一ID是核心需求之一。美团开源的Leaf提供了两种分布式ID生成方案:号段模式(高可用、依赖数据库)和Snowflake模式(高性能、去中心化)。本文将手把手教你如何配置和使用这两种…...
性能测试过程实时监控分析
性能监控 前言一、查看性能测试结果的3大方式1、GUI界面报告插件2、命令行运行 html报告3、后端监听器接入仪表盘 二、influxDB grafana jmeter测试监控大屏1、原理:2、linux环境中influxDB 安装和配置3、jmerer后端监听器连接influxDB4、linux环境总grafana环境搭…...
【工具类】Java的 LocalDate 获取本月第一天和最后一天
博主介绍:✌全网粉丝22W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
Eclipse 创建 Java 类
Eclipse 创建 Java 类 引言 Eclipse 是一款功能强大的集成开发环境(IDE),被广泛用于 Java 开发。本文将详细介绍如何在 Eclipse 中创建 Java 类,包括配置开发环境、创建新项目、添加类以及编写类代码等步骤。 配置 Eclipse 开发环境 1. 安装 Eclipse 首先,您需要在您…...
Centos编译升级libcurl
Centos编译升级libcurl 下载最新版源码包安装编译依赖配置编译选项如果报错:通过 EPEL 仓库安装手动源码编译安装 如果报错:安装Brotli 开发库 如果报错:方法一:安装 libpsl-devel 依赖通过 EPEL 仓库安装重新运行 configure 方…...
蓝桥杯第九天 2022 省赛 第 4 题 最少刷题数
太多坑了,考虑不全只能过50%,有两种特殊情况 public static void main(String[]args) {Scanner scan new Scanner(System.in);int n scan.nextInt();int a[] new int [100005];int b[] new int [100005];for(int i 0;i<n;i)a[i] scan.nextInt()…...
3D点云数据处理中的聚类算法总结
1.欧式聚类: 基于点的空间距离(欧几里得距离)来分割点云,将距离较近的点归为同一簇。 欧式聚类需要的参数:邻域半径R,簇的最小点阈值minPts,最大点数阈值maxPts。 实现效率: O(n * log n) 实现…...
配置本机监控
配置本机监控 1、安装zabbix-agent 2、编辑zabbix-agent配置文件 zabbix-agent工作模式: 主动模式 被动模式 这两行配置都是指定监控服务器的地址 被动模式下,zabbix server的地址 主动模式下,zabbix server的地址 指定被监控端的名称&…...
基于python的Flask模块化设计与蓝图的妙用——打造轻量化Web应用
基于python的Flask模块化设计与蓝图的妙用——打造轻量化Web应用 前言 如果你刚开始学习Flask,可能会遇到这样的困惑:当项目功能越来越多,代码都堆在一个.py文件里,不仅难维护,还容易冲突。别担心!本文将用…...
历年云南大学计算机复试上机真题
历年云南大学计算机复试机试真题 在线评测:传送门:pgcode.cn 喝饮料 题目描述 商店里有 n 中饮料,第 i 种饮料有 mi 毫升,价格为 wi。 小明现在手里有 x 元,他想吃尽量多的饮料,于是向你寻求帮助&#x…...
Python 线程池
Python 线程池 flyfish 线程池的概念 线程池是一种多线程处理形式,它预先创建了一定数量的线程,这些线程会被保存在一个线程池中。当有新的任务提交时,线程池会从池中取出一个空闲的线程来执行该任务;若池中没有空闲线程&#…...
【Linux】Bash是什么?怎么使用?
李升伟 整理 什么是 Bash? Bash(Bourne Again Shell)是一种 命令行解释器(Shell),广泛用于 Unix 和 Linux 操作系统。它是 Bourne Shell(sh) 的增强版,提供了更多的功能…...
蓝桥杯day2:解码异或 后的数组
一、题意 未知 整数数组 arr 由 n 个非负整数组成。 经编码后变为长度为 n - 1 的另一个整数数组 encoded ,其中 encoded[i] arr[i] XOR arr[i 1] 。例如,arr [1,0,2,1] 经编码后得到 encoded [1,2,3] 。 给你编码后的数组 encoded 和原数组 arr …...
R语言软件配置(自用)
①输入R: The R Project for Statistical Computing ②点击进入Cran镜像网页,选择清华大学镜像,选择自己合适的版本下载即可(以我电脑windows为例)。 ③点击base或者install R for the first time,然后选择Download R-4.4.3 for windows&…...
基于deepseek的智能语音客服【第二讲】后端异步接口调用封装
本篇内容主要讲前端请求(不包含)访问后端服务接口,接口通过检索知识库,封装提示词,调用deepseek的,并返回给前端的全过程,非完整代码,不可直接运行。 1.基于servlet封装异步请求 为…...
LEDNet总结
LEDNet:联合低光增强和暗光去模糊 1、暗光增强和去模糊可以单独处理,但是合并效果不理想。 研究问题的背景:光线不足 可见度颜色失真 最小快门速度有限 长时间曝光引起运动模糊 低光运动模糊同时存在 存在问题:暗光增强后运动模…...
线性规划的标准形式
标准形式的定义 目标函数:最大化线性目标函数 其中,x 是决策变量向量,c 是目标系数向量。 约束条件:等式形式约束 A x b, 其中,A 是约束系数矩阵,b 是常数项向量。 变量非负约束: 。 因此…...
xxl-job 执行器端服务器的简单搭建
xxl-job 执行器端服务器的简单搭建 先讲一下我们平时怎么使用 xxl-job 的,再引出背后是如何实现的。 我觉得对于一款成功的框架来说,好用,是非常重要的一个特性。 框架要便于接入,便于使用。对于用户来说,不要有太多…...
数字化企业可能会用到的系统有哪些?
你是否也曾像我一样,对这些问题充满疑问: 在企业数字化转型的浪潮中,究竟会涉及哪些系统? 这些系统又分别在何种情境下被投入使用? 如果你也有这样的疑问,那么这篇文章或许能为你答疑解惑。 为了给你一…...
【Linux】:守护进程化
朋友们、伙计们,我们又见面了,本期来给大家带来守护进程相关的知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到精通 数据结…...
在 web 部署 YOLOv8目标检测(Django+html)
本文介绍如何将自己训练好的模型在网页上进行应用,使用 Django html 进行部署,能够对视频和图像进行识别,并显示到页面上,下面是一个效果: 上 传 和另外 7 个页面 - 个人 - Microsoft Edge 2025-03-13 21-52-06 下面进…...
程序员学商务英语之Making Business Calls
Dialogue-1 Reaching Somebody By Telephone电话找人 A: What do you think the secrect to success is? 你认为成功的秘诀是什么? B: Hold on to your dreams. 坚持你的梦想。 A: May I speak to your boss, Mr. Wong? 请你的老板,王先生接电话?…...
java项目之基于ssm的少儿编程在线培训系统(源码+文档)
项目简介 少儿编程在线培训系统实现了以下功能: 用户信息管理: 用户信息新增 用户信息修改 教师信息管理: 教师信息添加 教师信息删除 教师信息修改 课程信息管理: 课程信息添加 课程信息修改 课程信息删除 课程类型管理&…...
【初学者】Python语言中有没有指针类型?
李升伟 整理 在Python语言中,没有像C或C那样的显式指针类型。Python的设计哲学强调简洁和易读,因此它隐藏了许多底层的细节,包括指针。 不过,Python中的变量可以被视为对对象的引用。当你创建一个对象并将其赋值给一个变量时&am…...
RG-S3760应用协议配置
RG-S3760应用协议配置 1. dhcp 服务配置 提问:如何在设备上开启dhcp 服务,让不同VLAN 下的电脑获得相应的IP 地址? 回答: 步骤一:配置VLAN 网关IP 地址,及将相关端口划入相应的VLAN 中 S3760#con t S…...
C++基础 [八] - list的使用与模拟实现
目录 list的介绍 List的迭代器失效问题 List中sort的效率测试 list 容器的模拟实现思想 模块分析 作用分析 list_node类设计 list 的迭代器类设计 迭代器类--存在的意义 迭代器类--模拟实现 模板参数 和 成员变量 构造函数 * 运算符的重载 运算符的重载 -- 运…...
skywalking微服务链路追踪
是什么? skywalking是一个优秀的国产开源框架,2015年由个人吴晟(华为开发者)开源 , 分布式链路追踪就是将一次分布式请求还原成调用链路,将一次分布式请求的调用情况集中展示,比如各个服务节点…...
K8S学习之基础三十七:prometheus监控node资源
Prometheus v2.2.1 编写yaml文件,包含创建ns、configmap、deployment、service # 创建monitoring空间 vi prometheus-ns.yaml apiVersion: v1 kind: Namespace metadata:name: monitor-sa# 创建SA并绑定权限 kubectl create serviceaccount monitor -n monito…...
Web 小项目: 网页版图书管理系统
目录 最终效果展示 代码 Gitee 地址 1. 引言 2. 留言板 [热身小练习] 2.1 准备工作 - 配置相关 2.2 创建留言表 2.3 创建 Java 类 2.4 定义 Mapper 接口 2.5 controller 2.6 service 3. 图书管理系统 3.1 准备工作 - 配置相关 3.2 创建数据库表 3.2.1 创建用户表…...
1221. 四平方和 -蓝桥杯真题-哈希函数思想
原题链接:1221. 四平方和 - AcWing题库 四平方和定理,又称为拉格朗日定理: 每个正整数都可以表示为至多 44 个正整数的平方和。 如果把 00 包括进去,就正好可以表示为 44 个数的平方和。 比如: 对于一个给定的正整…...
为什么要学习人工智能(AI)?—— 未来已来,AI引领时代变革
未来已来,AI引领时代变革 在这个日新月异的时代,人工智能(AI)正以不可阻挡之势重塑着我们的世界。从教育的深耕细作到科研的突破创新,从行政的效率提升到管理的智慧化转型,AI技术如同一股强大的潮流&#x…...
Markdig:强大的 .NET Markdown 解析器详解
在现代开发中,Markdown 已经成为了一种广泛使用的轻量级标记语言,特别是在文档、博客和内容管理系统中,Markdown 为开发者提供了快速、简洁的格式化文本方式。而在 .NET 生态中,Markdig 是一款非常强大的 Markdown 解析器…...
云计算迁移革命:企业如何摆脱“单一云”锁定,构建自主云未来?
一场价值690亿美元的行业地震 2023年,博通(Broadcom)以690亿美元完成对VMware的收购,这不仅是企业IT历史上的一次天价并购,更在全球云计算市场掀起了一场深远的地震。VMware长期以来是企业数据中心的核心支柱…...
蓝桥杯篇---按键长按与双击
文章目录 前言1. 新增全局变量和宏定义解释1.1宏定义KEY_EVENT_*DEBOUNCE_TIMEHOLD_TIMEDOUBLE_TIMEMULTI_TIME 1.2全局变量Key_ValKey_OldKey_DownKey_Upsys_tickkey_eventkey_pressedkey_press_startkey_last_releaseclick_cnt 2. 定时器初始化(1ms中断࿰…...
created在vue3 script setup中的写法
在 Vue 2 里,created 是一个生命周期钩子函数,会在实例已经创建完成之后被调用,主要用于在实例初始化之后、数据观测和 event/watcher 事件配置之前执行代码。而在 Vue 3 的 <script setup> 语法糖里,不再有像 Vue 2 那样直…...
基于springboot的房屋租赁系统(008)
摘 要 社会的发展和科学技术的进步,互联网技术越来越受欢迎。网络计算机的生活方式逐渐受到广大人民群众的喜爱,也逐渐进入了每个用户的使用。互联网具有便利性,速度快,效率高,成本低等优点。 因此,构建符…...
Linux上的`i2c-tools`工具集的编译构建和安装
源码复制到Ubuntu系统中并解压 的i2c-tools工具集的源码百度网盘下载链接: https://pan.baidu.com/s/1XNuMuT1auT1dMzYo3LAFmw?pwdi6xe 终端进入源码目录 cd /home/book/mybuild/i2c-tools-4.2执行编译构建命令 运行下面的命令进行编译构建 make CC${CROSS_COM…...
java项目之基于ssm的社区流浪动物救助领养系统
项目简介 社区流浪动物救助领养系统实现了以下功能: 本社区流浪动物救助领养系统分为管理员还有用户两个权限,管理员可以管理用户的基本信息内容,可以管理回访信息以及回访的租赁信息,能够与用户进行相互交流等操作,…...
网络空间安全(34)安全防御体系
前言 安全防御体系是一个多层次、多维度的系统,旨在保护组织或个人的信息资产免受各种网络攻击和威胁。 一、技术层面 网络边界防御 防火墙:部署在网络边界,通过设定规则允许或阻止特定流量的进出,保护内部网络不受外部攻击。入侵…...
《Linux 网络架构:基于 TCP 协议的多人聊天系统搭建详解》
一、系统概述 本系统是一个基于 TCP 协议的多人聊天系统,由一个服务器和多个客户端组成。客户端可以连接到服务器,向服务器发送消息,服务器接收到消息后将其转发给其他客户端,实现多人之间的实时聊天。系统使用 C 语言编写&#x…...
知识蒸馏:让大模型“瘦身”的魔法
知识蒸馏:让大模型“瘦身”的魔法 什么是蒸馏模型?AI界的“知识浓缩术”核心定义传统训练 vs 知识蒸馏关键优势 DeepSeek的蒸馏“三步魔法”骨架提取——搭建“迷你版大脑”知识灌注——模仿教师的“思考过程”微调优化——针对场景“查漏补缺” DeepSee…...
MySQL数据库精研之旅第一期:开启数据管理新旅程
专栏:MySQL数据库成长记 个人主页:手握风云 目录 一、数据库简介 1.1. 数据库的概念 1.2. 数据库和数据结构的关系 1.3. 主流数据库 1.3.1. 关系型数据库 1.3.2. 非关系型数据库 1.4. 关系型数据库的概念 二、MySQL配置 2.1. mysqld服务端程序 …...
Linux复习——基础IO,认识文件描述符、软硬件链接
1.复习C文件接口 1.1 fopen FILE *fopen(const char *path, const char *mode); path:带路径的文件名称(待打开的文件) mode: r:以可读方式打开,不可写,文件不存在,则报错 r&…...
【Java集合夜话】第1篇:拨开迷雾,探寻集合框架的精妙设计
欢迎来到Java集合框架系列的第一篇文章!🌹 本系列文章将以通俗易懂的语言,结合实际开发经验,带您深入理解Java集合框架的设计智慧。🌹 若文章中有任何不准确或需要改进的地方,欢迎大家指出,让我…...
Prometheus使用
介绍:Prometheus 是一个开源的 监控与告警系统,主要用于采集和存储时间序列数据(Time Series Data) Prometheus的自定义查询语言PromQL Metric类型 为了能够帮助用户理解和区分这些不同监控指标之间的差异,Prometheu…...