/** * A {@link io.netty.channel.socket.ServerSocketChannel} implementation which uses * NIO selector based implementation to accept new connections. */ publicclassNioServerSocketChannelextendsAbstractNioMessageChannel implementsio.netty.channel.socket.ServerSocketChannel{
privatestatic ServerSocketChannel newSocket(SelectorProvider provider){ try { /** * Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in * {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise. * * See <a href="https://github.com/netty/netty/issues/2308">#2308</a>. */ return provider.openServerSocketChannel(); } catch (IOException e) { thrownew ChannelException( "Failed to open a server socket.", e); } }
privatefinal ServerSocketChannelConfig config;
/** * Create a new instance */ publicNioServerSocketChannel(){ this(newSocket(DEFAULT_SELECTOR_PROVIDER)); }
/** * Create a new instance using the given {@link SelectorProvider}. */ publicNioServerSocketChannel(SelectorProvider provider){ this(newSocket(provider)); }
/** * Create a new instance using the given {@link ServerSocketChannel}. */ publicNioServerSocketChannel(ServerSocketChannel channel){ super(null, channel, SelectionKey.OP_ACCEPT); config = new NioServerSocketChannelConfig(this, javaChannel().socket()); } ... }
The RecvByteBufAllocator that automatically increases and decreases the predicted buffer size on feed back. It gradually increases the expected number of readable bytes if the previous read fully filled the allocated buffer. It gradually decreases the expected number of readable bytes if the read operation was not able to fill a certain amount of the allocated buffer two times consecutively. Otherwise, it keeps returning the same prediction.
构造方法,默认是1024,最小是63,最大是65536
/** * Creates a new predictor with the default parameters. With the default * parameters, the expected buffer size starts from {@code 1024}, does not * go down below {@code 64}, and does not go up above {@code 65536}. */ publicAdaptiveRecvByteBufAllocator(){ this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM); }
A list of ChannelHandlers which handles or intercepts inbound events and outbound operations of a Channel. ChannelPipeline implements an advanced form of the Intercepting Filter pattern to give a user full control over how an event is handled and how the ChannelHandlers in a pipeline interact with each other.
Creation of a pipeline
Each channel has its own pipeline and it is created automatically when a new channel is created.
How an event flows in a pipeline
The following diagram describes how I/O events are processed by ChannelHandlers in a ChannelPipeline typically. An I/O event is handled by either a ChannelInboundHandler or a ChannelOutboundHandler and be forwarded to its closest handler by calling the event propagation methods defined in ChannelHandlerContext, such as ChannelHandlerContext.fireChannelRead(Object) and ChannelHandlerContext.write(Object).
An inbound event is handled by the inbound handlers in the bottom-up direction as shown on the left side of the diagram. An inbound handler usually handles the inbound data generated by the I/O thread on the bottom of the diagram. The inbound data is often read from a remote peer via the actual input operation such as SocketChannel.read(ByteBuffer). If an inbound event goes beyond the top inbound handler, it is discarded silently, or logged if it needs your attention.
An outbound event is handled by the outbound handler in the top-down direction as shown on the right side of the diagram. An outbound handler usually generates or transforms the outbound traffic such as write requests. If an outbound event goes beyond the bottom outbound handler, it is handled by an I/O thread associated with the Channel. The I/O thread often performs the actual output operation such as SocketChannel.write(ByteBuffer)
For example, let us assume that we created the following pipeline:
ChannelPipeline p = ...; p.addLast("1", new InboundHandlerA()); p.addLast("2", new InboundHandlerB()); p.addLast("3", new OutboundHandlerA()); p.addLast("4", new OutboundHandlerB()); p.addLast("5", new InboundOutboundHandlerX());
In the example above, the class whose name starts with Inbound means it is an inbound handler. The class whose name starts with Outbound means it is a outbound handler.
In the given example configuration, the handler evaluation order is 1, 2, 3, 4, 5 when an event goes inbound. When an event goes outbound, the order is 5, 4, 3, 2, 1. On top of this principle, ChannelPipeline skips the evaluation of certain handlers to shorten the stack depth:
3 and 4 don’t implement ChannelInboundHandler, and therefore the actual evaluation order of an inbound event will be: 1, 2, and 5.
1 and 2 don’t implement ChannelOutboundHandler, and therefore the actual evaluation order of a outbound event will be: 5, 4, and 3.
If 5 implements both ChannelInboundHandler and ChannelOutboundHandler, the evaluation order of an inbound and a outbound event could be 125 and 543 respectively.
Forwarding an event to the next handler
As you might noticed in the diagram shows, a handler has to invoke the event propagation methods in ChannelHandlerContext to forward an event to its next handler. Those methods include:
A user is supposed to have one or more ChannelHandlers in a pipeline to receive I/O events (e.g. read) and to request I/O operations (e.g. write and close). For example, a typical server will have the following handlers in each channel’s pipeline, but your mileage may vary depending on the complexity and characteristics of the protocol and business logic:
Protocol Decoder - translates binary data (e.g. ByteBuf) into a Java object.
Protocol Encoder - translates a Java object into binary data.
Business Logic Handler - performs the actual business logic (e.g. database access).
and it could be represented as shown in the following example:
staticfinal EventExecutorGroup group = new DefaultEventExecutorGroup(16); ...
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new MyProtocolDecoder()); pipeline.addLast("encoder", new MyProtocolEncoder());
// Tell the pipeline to run MyBusinessLogicHandler's event handler methods // in a different thread than an I/O thread so that the I/O thread is not blocked by // a time-consuming task. // If your business logic is fully asynchronous or finished very quickly, you don't // need to specify a group. pipeline.addLast(group, "handler", new MyBusinessLogicHandler());
注:可以使用重载这个方法添加一个事件循环组 group 去执行耗时的任务,获取在 MyBusinessLogicHandler 中把耗时部分异步处理,这样就不会堵塞 IO 线程
Thread safety
A ChannelHandler can be added or removed at any time because a ChannelPipeline is thread safe. For example, you can insert an encryption handler when sensitive information is about to be exchanged, and remove it after the exchange.
io.netty.channel public class ChannelOption extends AbstractConstant<ChannelOption>
A ChannelOption allows to configure a ChannelConfig in a type-safe way. Which ChannelOption is supported depends on the actual implementation of ChannelConfig and may depend on the nature of the transport it belongs to.
Type parameters: - the type of the value which is valid for the ChannelOption
/** * Returns the {@link Constant} which is assigned to the specified {@code name}. * If there's no such {@link Constant}, a new one will be created and returned. * Once created, the subsequent calls with the same {@code name} will always return the previously created one * (i.e. singleton.) * * @param name the name of the {@link Constant} */ public T valueOf(String name){ checkNotNullAndNotEmpty(name); return getOrCreate(name); }
/** * Get existing constant by name or creates new one if not exists. Threadsafe * * @param name the name of the {@link Constant} */ private T getOrCreate(String name){ T constant = constants.get(name); if (constant == null) { final T tempConstant = newConstant(nextId(), name); constant = constants.putIfAbsent(name, tempConstant); if (constant == null) { return tempConstant; } }
return constant; }
/** * Returns {@code true} if a {@link AttributeKey} exists for the given {@code name}. */ publicbooleanexists(String name){ checkNotNullAndNotEmpty(name); return constants.containsKey(name); }
/** * Creates a new {@link Constant} for the given {@code name} or fail with an * {@link IllegalArgumentException} if a {@link Constant} for the given {@code name} exists. */ public T newInstance(String name){ checkNotNullAndNotEmpty(name); return createOrThrow(name); }
/** * Creates constant by name or throws exception. Threadsafe * * @param name the name of the {@link Constant} */ private T createOrThrow(String name){ T constant = constants.get(name); if (constant == null) { final T tempConstant = newConstant(nextId(), name); constant = constants.putIfAbsent(name, tempConstant); if (constant == null) { return tempConstant; } }
thrownew IllegalArgumentException(String.format("'%s' is already in use", name)); }
/** * Get existing constant by name or creates new one if not exists. Threadsafe * * @param name the name of the {@link Constant} */ private T getOrCreate(String name){ T constant = constants.get(name); if (constant == null) { final T tempConstant = newConstant(nextId(), name); constant = constants.putIfAbsent(name, tempConstant); if (constant == null) { return tempConstant; } }
return constant; }
这里使用了双重检验机制,这个常量池保存的值是一个 T extends Constant<T> 包装过后的
io.netty.util public final class AttributeKey extends AbstractConstant<AttributeKey>
Key which can be used to access Attribute out of the AttributeMap. Be aware that it is not be possible to have multiple keys with the same name.
Type parameters: - the type of the Attribute which can be accessed via this AttributeKey.
与 ChannelOption 很相似,AttributeMap ,AttributeKey ,Attribute 相当一个 Map,key 和 value
/** * Holds {@link Attribute}s which can be accessed via {@link AttributeKey}. * * Implementations must be Thread-safe. */ publicinterfaceAttributeMap{ /** * Get the {@link Attribute} for the given {@link AttributeKey}. This method will never return null, but may return * an {@link Attribute} which does not have a value set yet. */ <T> Attribute<T> attr(AttributeKey<T> key);
/** * Returns {@code} true if and only if the given {@link Attribute} exists in this {@link AttributeMap}. */ <T> booleanhasAttr(AttributeKey<T> key); }
io.netty.channel @Sharable public abstract class ChannelInitializer extends ChannelInboundHandlerAdapter
A special ChannelInboundHandler which offers an easy way to initialize a Channel once it was registered to its EventLoop. Implementations are most often used in the context of Bootstrap.handler(ChannelHandler) , ServerBootstrap.handler(ChannelHandler) and ServerBootstrap.childHandler(ChannelHandler) to setup the ChannelPipeline of a Channel.
// If the registered is false it means that the channel was not registered on an eventloop yet. // In this case we add the context to the pipeline and add a task that will call // ChannelHandler.handlerAdded(...) once the channel is registered. if (!registered) { newCtx.setAddPending(); callHandlerCallbackLater(newCtx, true); returnthis; }