Netty Notes

Netty core -
including:

   Netty 's technical and architectural aspects
    Channel, EventLoop and ChannelFuture
    ChannelHandler and ChannelPipeline
    Bootstrapping


    Channel-Sockets
    EventLoop-control flow, multithreading, concurrent
    ChannelFuture-asynchronous notification



channel:
basic I/O operations (bind(), connect(), read() and write()) rely on primitives provided by the underlying network transport layer. In Java-based network programming, the basic structural unit is the Socket class. Netty's Channel interface provides an API, which greatly reduces the complexity of using Socket directly.

Channel implementation:

    EmbeddedChannel
    LocalServerChannel
    NioDatagramChannel
    NioSctpChannel
    NioSocketChannel


channel Corresponding method; Channel is thread-safe, so it can be used in multi-threaded situations. The specified client writes data,
* eventLoop returns the assigned EventLoop to the Channel
Pipeline returns the assigned ChannelPipeline to the Channel
isActive Returns true if the Channel is active. The meaning of active may depend on the underlying transport. For example, a Socket transfer becomes active when it is connected to the remote end, and a Datagram transfer becomes active when it starts.
localAddress returns the local SocketAddress
remoteAddress returns the remote SocketAddress
write Write data to the remote end. The data is sent to the ChannelPipeline and then queued until it is flushed
. The data written before flushing goes to the underlying transport layer. For example,
writeAndFlush in a Socket calls the convenient methods of write() and flush()


successively EventLoop:
EventLoop defines the core of Netty Abstraction for handling events that occur during the life cycle of a connection. We will discuss EventLoop in detail when we talk about Netty's threading model in Chapter 7.


    An EventLoopGroup contains one or more EventLoops.
    All EventLoops are bound to a thread for their entire life cycle.
    All events handled by an EventLoop are handled by A Channel processed by an EventLoop-bound thread
    is only registered with one EventLoop during its life cycle.
    An EventLoop can be assigned to one or more Channels.


Implementation details:
The excellent performance of the Netty threading model depends on determining who the current thread of execution is; that is, whether it is the thread bound to the current Channel and EventLoop. (Recall that EventLoop is responsible for handling all events of a Channel throughout its lifetime.)

If the current calling thread is the EventLoop assigned to the Channel, the code is executed. Otherwise, EventLoop puts the task into an internal queue for delayed execution. When EventLoop processes its events, it executes the tasks in the queue. This explains why any thread can directly interact with the Channel without adding synchronization to the ChannelHandler.

http://ifeve.com/netty-in-action-7/

ChannelFuture:
All I/O operations in etty are asynchronous. Because an operation may not return immediately, we need a method to determine its result at a later time. Therefore, Netty provides ChannelFuture, and its addListener() method registers a ChannelFutureListener. When the operation is completed, you can receive a notification (whether successful or not)
corresponding to the future in java, which is used to track asynchronous operations, which can be considered as the occupation of asynchronous operations. Bit symbol, and use

ChannelHandler in conjunction with the callback function:
Life cycle:
The life cycle of the channel:
ChannelUnregistered Channel has been created and has not been registered on an EventLoop.
ChannelRegistered Channel has been registered on an EventLoop
ChannelActive Channel is active (connected to a remote end) and can send and receive data.
ChannelInactive Channel is not connected to the remote end. The
corresponding handler method,
handlerAdded is called when ChannelHandler is added to a ChannelPipeline
handlerRemoved is moved from a ChannelPipeline When
exceptionCaught is called, the two ChannelHandler sub-interfaces defined by Netty are called when an error occurs in the ChannelPipeline during processing. There are

many methods. You can use adapters. You can use the two classes ChannelInboundHandlerAdapter and ChannelOutboundHandlerAdapter. These two adapter classes provide the basic implementations of ChannelInboundHandler and ChannelOutboundHandler respectively. They inherit the methods of the common parent interface ChannelHandler and extend the abstract class ChannelHandlerAdapter.
    ChannelInboundHandler - handles incoming data and all types of state changes; API:
     channelRegistered is called when a Channel is registered with the EventLoop and can handle I/O
channelUnregistered When a Channel is unregistered from its EventLoop and no longer handles I/O Called when channelActive is called
when the Channel becomes active; the Channel is connected/bound, ready
channelInactive Called when the Channel leaves the active state and is no longer connected to a remote end
channelReadComplete Called when a read operation on the Channel is completed
channelRead Called when data is read from the Channel
channelWritabilityChanged When the channel's writable state changes is called. With this method, the user can ensure that writes do not proceed too fast (to avoid OutOfMemoryError) or continue writing when the Channel becomes writable again. The isWritable() method of the Channel class can be used to check the writable state of the Channel. The writability threshold can be set by Channel.config().setWriteHighWaterMark() and Channel.config().setWriteLowWaterMark().
userEventTriggered ChannelOutboundHandler is called when ChannelnboundHandler.fireUserEventTriggered() is triggered by a POJO passing through ChannelPipeline -


    processing output data, all operations can be intercepted; API
    bind(ChannelHandlerContext,SocketAddress,ChannelPromise) Request to bind Channel to a local address
connect(ChannelHandlerContext, SocketAddress,SocketAddress,ChannelPromise) request to connect Channel to the remote end
disconnect(ChannelHandlerContext, ChannelPromise) Request to disconnect the Channel from the remote end
close(ChannelHandlerContext, ChannelPromise) Request to close the Channel
deregister(ChannelHandlerContext, ChannelPromise) Request the Channel to deregister from its EventLoop
read(ChannelHandlerContext) Request to read more data from the Channel
flush(ChannelHandlerContext) Request to flush queue data to remote through Channel
write(ChannelHandlerContext, Object, ChannelPromise) Request to write data to remote through Channel


From the perspective of application developers, the main component of Netty is ChannelHandler, which acts as an application logic A container that handles input and output data. This is possible because ChannelHandler's methods are ChannelHandlerContext s that are triggered by network events (events here in a broad sense)

:
The handler's context, environment, ChannelHandlerContext represents the relationship between a ChannelHandler and a ChannelPipeline, which is created when the ChannelHandler is added to the ChannelPipeline. The main function of ChannelHandlerContext is to manage the interaction between its corresponding ChannelHandler and other ChannelHandlers belonging to the same ChannelPipeline. ChannelHandlerContext has many methods, some of which Channel and ChannelPipeline also have, but there are some differences. If you call these methods on a Channel or ChannelPipeline instance, their calls will traverse the entire pipeline. The same method called on ChannelHandlerContext only starts from the current ChannelHandler and goes to the next ChannelHandler in the pipeline that can handle this event.

ChannelHandlerContext API:
bind bind to the given SocketAddress, return a ChannelFuture
channel return the bound Channel
close close the Channel, return a ChannelFuture
connect connect to the given SocketAddress, return a ChannelFuture
deregister Deregister from the previously allocated EventExecutor, return A ChannelFuture
disconnect disconnects from the remote end and returns a ChannelFuture
executor Returns the EventExecutor that dispatches the event
fireChannelActive triggers calling channelActive() of next ChannelInboundHandler (connected)
fireChannelInactive triggers calling channelInactive() of next ChannelInboundHandler (disconnected)
fireChannelRead triggers calling channelRead() of next ChannelInboundHandler (message received)
fireChannelReadComplete triggers channelWritabilityChanged event to next A ChannelInboundHandler
handler returns the bound ChannelHandler
isRemoved Returns true if the bound ChannelHandler has been removed from the ChannelPipeline
name Returns the unique name of this ChannelHandlerContext instance
Pipeline Returns the bound ChannelPipeline
read Reads data from the Channel to the first input buffer; if successful , trigger a channelRead event, notify the handler channelReadComplete
write to write messages through the pipeline






ChannelPipeline through this ChannelHandlerContext:
A ChannelPipeline provides a container for a chain of ChannelHandlers, and defines the API for transmitting input and output events in this chain of ChannelHandlers. When a Channel is created, it is automatically assigned a ChannelPipeline.

Execution and blocking of ChannelHandler
Usually each ChannelHandler in the ChannelPipeline processes events passed to it through its EventLoop (I/O thread). It is very important not to block this thread, because blocking may negatively affect the overall I/O processing.

Sometimes, we will need to interact with existing code that calls blocking APIs. In this case, some add() methods of ChannelPipeline support EventExecutorGroup. If the event is sent to a custom EventExecutorGroup, it will be processed by an EventExecutor in the EventExecutorGroup, thus leaving the Channel's EventLoop. For this scenario, Netty provides a specific implementation called DefaultEventExecutorGroup.

ChannelPipeline's ChannelHandler related methods:
addFirstaddBeforeaddAfteraddLast Add a ChannelHandler to ChannelPipeline
remove Remove a ChannelHandler from ChannelPipeline
replace Replace another ChannelHandler in ChannelPipeline with a ChannelHandler
get Returns a ChannelHandler by type or name
context Returns the ChannelHandlerContext
names bound to the ChannelHandler Returns the names of all ChannelHandlers in the

ChannelPipeline ChannelPipeline input methods:
fireChannelRegistered Calls the channelRegistered(ChannelHandlerContext)
of the next ChannelInboundHandler in the ChannelPipeline fireChannelUnregistered Calls the channelUnRegistered(ChannelHandlerContext) of the next ChannelInboundHandler in the
ChannelPipeline fireChannelActive Calls the next ChannelPipeline channelActive(ChannelHandlerContext) of ChannelInboundHandler fireChannelInactive
calls channelInactive(ChannelHandlerContext) of the next ChannelInboundHandler in the
ChannelPipeline fireExceptionCaught Calls the exceptionCaught(ChannelHandlerContext,Throwable) of the next ChanneHandler in the ChannelPipeline
fireUserEventTriggered Calls userEventTriggered(ChannelHandlerContext, Object)
of the next ChannelInboundHandler in ChannelPipeline fireChannelRead Calls channelRead(ChannelHandlerContext, Object msg) of the next ChannelInboundHandler in ChannelPipeline
fireChannelReadComplete Calls channelReadComplete(ChannelHandlerContext) of the next ChannelStateHandler in

ChannelPipeline ChannelPipeline output operation
bind Bind Channel to a local address. This will call bind(ChannelHandlerContext, SocketAddress, ChannelPromise) of the next ChannelOutboundHandler in the ChannelPipeline
to connect the Channel to a remote address. This will call the next ChannelOutboundHandler in the ChannelPipeline connect(ChannelHandlerContext, SocketAddress, ChannelPromise)
disconnect to disconnect the Channel. This will call disconnect(ChannelHandlerContext, ChannelPromise) of the next ChannelOutboundHandler in the ChannelPipeline
close Closes the Channel. This calls close(ChannelHandlerContext,ChannelPromise)
deregister of the next ChannelOutboundHandler in the ChannelPipeline to deregister the Channel from its previously allocated EventLoop. This will call the deregister(ChannelHandlerContext, ChannelPromise) of the next ChannelOutboundHandler in the ChannelPipeline to
flush all the data to be written by the Channel. This will call flush(ChannelHandlerContext) write of the next ChannelOutboundHandler in the ChannelPipeline
to write a message to the Channel. This will call the write(ChannelHandlerContext, Object msg, ChannelPromise) of the next ChannelOutboundHandler in the ChannelPipeline Note: The message will not be written to the underlying Socket, just queued. If you want to write to Socket, call flush() or writeAndFlush()
writeAndFlush This is a convenient method to call write() and flush() successively.
read requests to read more data from the Channel. This calls the read(ChannelHandlerContext) Bootstrapping of the next ChannelOutboundHandler in the ChannelPipeline :



Netty's bootstrap class provides a container for an application's network layer configuration, including binding a process to a given port, or connecting a process to another process running on a specific host and port.
Type Bootstrap ServerBootstrap
network function is connected to a remote host and the port is bound to a local port
. EventLoopGroups number 1 2


netty transport mode
The transport supports multiple modes: corresponding private final EventLoopGroup bossGroup = new NioEventLoopGroup(BIZGROUPSIZE);//non-blocking mode, OioEventLoopGroup is blocking and b.channel(NioServerSocketChannel.class);//non-blocking, OIO(Old-Blocking-IO.) blocking; NioDatagramChannel UDP related
NIO io.netty.channel.socket.nio adopts java.nio.channels package, a Selector-based approach.
Epoll io.netty.channel.epoll uses epoll()'s JNI and non-blocking IO. This mode supports features that are only available on Linux platforms, such as SO_REUSEPORT, is faster than the NIO mode, and is completely non-blocking.
OIO io.netty.channel.socket.oio uses the java.net package and uses blocking streams.
Local io.netty.channel.local Local transport can be used for intra-VM channel communication.
Embedded io.netty.channel.embedded Embedded transport can use ChannelHandler which does not require real network transport. Very useful for testing your ChannelHandler.


OP_ACCEPT Requests notification when a new connection is accepted and a Channel is created
OP_CONNECT Requests notification when a connection is established
OP_READ Requests notification when data is ready to be read from the Channel
OP_WRITE Requests notification when available to a Channel Request to be notified when more data is written in. This is used when the socket buffer is completely full, which usually happens when the data transfer is much faster than the remote processing.

Transport TCP UDP SCTP* UDT
NIO XXXX
Epoll(Linux only) XX ——
OIO XXXX  netty ByteBuf ByteBuf-Netty's data container, replacing ByteBuffer, this powerful implementation breaks through the limitations of JDK API and provides better network developers API. ByteBuf contains two different indexes: one for reading and one for writing. When you read data from a ByteBuf, its readerIndex is incremented by the number of bytes read. Likewise, when you write data to a ByteBuf, its writerIndex is incremented. Figure 5.1 is the layout and state diagram of an empty ByteBuf, both of which are 0. In addition, there is a capacity index, which is the largest. When the start is 0, it is the tail when read and write overlap. When write and capacity overlap, it cannot be wrote.












Read/Write operations:
get() and set() operations start from a specified index and do not change the value of the index
read() and write() operations start from a specified index, with the bytes read/written Number adjustment index value

get method
getBoolean(int) Returns the boolean value at the specified index position
getByte(int) Returns the byte value at the specified index position
getUnsignedByte(int) Returns the unsigned byte value at the specified index position (the return type is short)
getMedium(int ) Returns the 24-bit (medium) value at the
specified index position getUnsignedMedium(int) Returns the unsigned 24-bit (medium) value at
the specified index position getInt(int) Returns the int value at the specified index position
getUnsignedInt(int) Returns None at the specified index position Signed int value (the return type is long)
getLong(int) returns the long value at the specified index position
getShort(int) returns the short value at the specified index position
getUnsignedShort(int) returns the unsigned short value at the specified index position (the return type is int)
getBytes(int, …) transfer buffer data to the target position starting from the specified index position?


set method
setBoolean(int, boolean) Set the boolean value at the specified index position
setByte(int index, int value) Set the Byte value at the specified index position
setMedium(int index, int value) Set the 24-bit (medium) value at the specified index position
setInt(int index, int value) Set the int value at the specified index position
setLong (int index, long value) set the long value at the specified index position
setShort(int index, int value) set the short value at the specified index position



read method;
readBoolean() returns the boolean value at the current readerIndex position, and then increments the readerIndex by 1
readByte() Returns the byte value of the current readerIndex position, then adds 1
to the readerIndex readUnsignedByte() returns the unsigned byte value of the current readerIndex position (the return type is short), then adds 1
to the readerIndex readMedium() Returns the 24-bit (medium) value of the current readerIndex position, Then readerIndex plus 3
readUnsignedMedium() returns the unsigned 24-bit (medium) value at the current readerIndex position, then readerIndex plus 3
readInt() returns the int value at the current readerIndex position, then readerIndex plus 4
readUnsignedInt() returns the unsigned int value of the current readerIndex position (return type is long), then readerIndex plus 4
readLong() returns the long value of the current readerIndex position, then readerIndex plus 8
readShort() returns the short value of the current readerIndex position, then readerIndex plus 2
readUnsignedShort() returns the unsigned short value at the current readerIndex position (return type is int), then readerIndex plus 2
readBytes(ByteBuf | byte[]

destination,

int dstIndex [,int length])
from the readerIndex position (if specified , the length is length bytes), transfer the current ByteBuf data to the target ByteBuf or byte[]. The readerIndex value of the current ByteBuf increases according to the number of bytes sent

write method
writeBoolean(boolean) Writes the boolean value at the current writerIndex position, then adds 1 to the writerIndex
writeByte(int) Writes the byte value to the current writerIndex position, then adds 1 to the
writerIndex writeMedium (int) Write the medium value at the current writerIndex position, then add 3 to the writerIndex
writeInt(int) Write the int value at the current writerIndex position, then add 4 to the writerIndex
writeLong(long) Write a long value at the current writerIndex position, then add 8 to writerIndex
writeShort(int) Write a short value to the current writerIndex position, then add 2 to writerIndex
writeBytes(source ByteBuf |

byte[] [,int srcIndex

,int length] ),
starting at the current writerIndex position, transfers data from the specified source ByteBuf or byte[] to the current ByteBuf. If srcIndex and length are provided in the input parameters, read length bytes starting from srcIndex. The writerIndex value of the current ByteBuf grows according to the number of bytes written.

More operations:
isReadable() returns true if at least one byte of data is readable
isWritable() returns true if at least one byte of data is writable
readableBytes() returns the number of readable bytes
writableBytes() returns writable bytes
capacity() returns the number of bytes that the ByteBuf can hold. After calling this method, ByteBuf will try to expand the capacity until it reaches maxCapacity()
maxCapacity() Returns the maximum number of bytes that ByteBuf can hold hasArray() Returns
true if ByteBuf has a byte array
Array() If ByteBuf has a byte array, return the array; otherwise, throw an exception, UnsupportedOperationException

ByteBuf allocation
ByteBufAllocator: On-demand allocation can get ByteBufAllocator from channel and channelctx allco = channel.alloc();; PoolByteBufAllocator and UnpooledByteBufAllocator . The former puts ByteBuf instances into the pool, which improves performance and reduces memory fragmentation to a minimum. This implementation employs an efficient strategy for memory allocation called jemalloc. It has been adopted by several modern operating systems. The latter does not put ByteBuf into the pool, and returns a new ByteBuf instance each time it is called.
api method
buffer(); buffer(int initialCapacity); buffer(int initialCapacity, int maxCapacity); return heap or direct ByteBuf
heapBuffer(); heapBuffer(int initialCapacity); heapBuffer(int initialCapacity, intmaxCapacity); return heap ByteBuf
directBuffer() ;directBuffer(int initialCapacity);directBuffer(int initialCapacity, intmaxCapacity);return direct ByteBuf


compositeBuffer(); compositeBuffer(int maxNumComponents); compositeDirectBuffer(); compositeDirectBuffer(int maxNumComponents ); compositeHeapBuffer
( ); compositeHeapBuffer(int maxNumComponents); ByteBuf utility class for socket I/O operations


: Unpooled buffers
Sometimes, you cannot get a reference to the ByteBufAllocator. In this case, Netty has a utility class called Unpooled that provides some static helper methods to create unpooled ByteBuf instances. Table 5.8 lists the most important of these methods.

API methods:
buffer(); buffer(int initialCapacity); buffer(int initialCapacity, int maxCapacity) returns heap ByteBuf
directBuffer(); directBuffer(int initialCapacity); directBuffer(int initialCapacity, intmaxCapacity) returns direct ByteBuf
wrappedBuffer() returns wrapped ByteBuf
copiedBuffer() returns the copied ByteBuf

utility class: ByteBufUtil
ByteBufUtil provides some static helper methods for manipulating ByteBuf. Because the ByteBufUtil API is generic and has nothing to do with the memory pool, these helper methods are implemented outside the buffer class


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326487001&siteId=291194637