In Netty
, the source of data is not the abstraction in Java OIO
, like Stream
, but Event
. One of obvious differences between Stream
& Event
is how we handle IO request. In OIO
, we read/write from Stream
and block if it is not available; In Netty
, we are notified when corresponding event happens. Netty
have much common in this design with Java NIO
.
In this blog, we will dive into Netty
's similarity between Java NIO
: event, channel, buffer.
Event
The following is event propagation methods we have introduced in Handler
part.
- Inbound event propagation methods:
- [
ChannelHandlerContext.fireChannelRegistered()
] - [
ChannelHandlerContext.fireChannelActive()
] - [
ChannelHandlerContext.fireChannelRead(Object)
] - [
ChannelHandlerContext.fireChannelReadComplete()
] - [
ChannelHandlerContext.fireExceptionCaught(Throwable)
] - [
ChannelHandlerContext.fireUserEventTriggered(Object)
] - [
ChannelHandlerContext.fireChannelWritabilityChanged()
] - [
ChannelHandlerContext.fireChannelInactive()
] - [
ChannelHandlerContext.fireChannelUnregistered()
]
- [
- Outbound event propagation methods:
- [
ChannelOutboundInvoker.bind(SocketAddress, ChannelPromise)
] - [
ChannelOutboundInvoker.connect(SocketAddress, SocketAddress, ChannelPromise)
] - [
ChannelOutboundInvoker.write(Object, ChannelPromise)
] - [
ChannelHandlerContext.flush()
] - [
ChannelHandlerContext.read()
] - [
ChannelOutboundInvoker.disconnect(ChannelPromise)
] - [
ChannelOutboundInvoker.close(ChannelPromise)
] - [
ChannelOutboundInvoker.deregister(ChannelPromise)
]
- [
In Java, we don’t have such clear definition of all kinds of IO events, NIO
just provides some const in SelectionKey
like following to do event dispatch:
OP_READ
OP_WRITE
OP_CONNECT
OP_ACCEPT
Future & Callback
In the event-driven world, we react via callback/listener. There is actually one more kind of callback except Handler
:
ChannelFuture future = ctx.write(res);
if (close) {
// after write is finished, then close
future.addListener(ChannelFutureListener.CLOSE);
}
As we have introduced above, we can trigger outbound events like write
, close
. Of course, those method is not blocking, so it return ChannelFuture
(Actually, almost all methods in ChannelOutboundInvoker
return ChannelFuture
). So, we can addListener
to react when previous action is done.
Sync vs Get
If we look close the method of ChannelFuture
, we can find a sync
method which
Waits for this future until it is done
So what’s the differences between get
of java.util.concurrent.Futre
?
- They are both wait and re-throw exception;
sync
return aFuture<V>
,get
returnV
, sosync
can chain channel IO operation like following shows;
b.bind(PORT).sync().channel().closeFuture().sync();
Channel
The following is the Channel
hierarchy for NIO selector based channel:
AbstractNioChannel
|
AbstractNioMessageChannel
| \
NioDatagramChannel NioServerSocketChannel
AbstractNioChannel
|
AbstractNioByteChannel
|
NioSocketChannel
The main differences between three concrete implementation (NioDatagramChannel
, NioServerSocketChannel
, NioSocketChannel
) is how they maps bytes into Java class when read/write, which affect our handler
process (If the type is not we wanted, we may need extra Encoder/Decoder
).
NioServerSocketChannel
is used to accept remote connection, so it will convert data to NioSocketChannel
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = SocketUtils.accept(javaChannel());
try {
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (...) {...}
NioSocketChannel
, on the other hand, is responsible for real communication, read data into ByteBuf
(Netty
buffer abstraction)
@Override
protected int doReadBytes(ByteBuf byteBuf) throws Exception {
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.attemptedBytesRead(byteBuf.writableBytes());
return byteBuf.writeBytes(javaChannel(), allocHandle.attemptedBytesRead());
}
NioDatagramChannel
will read data and convert to DatagramPacket
as #doReadMessages
shows.
Buffer
ByteBuf
As the official document introduced, the Netty ByteBuf
has three main advantages over NIO ByteBuffer
:
User friendliness
We have to flip
ByteBuffer
when we change from reading to writing or in reverse, which is very easy to forget and cause some error, this is because ByteBuffer
has only one index of our position. In contrast, ByteBuf
has two index, one for read, one for write.
Furthermore, it provides richer utility method:
it has accessor methods for signed and unsigned integers, searches, and strings.
Extensibility
This is easy to understand: ByteBuffer
can’t be extended as here explained. However, ByteBuf
can as it designed.
Performance
ByteBuf
is faster in following two aspects:
- It will not zeroing buffer when allocated;
- It’s allocation/deallocation is not relying on GC but reference count: this performance is gain is relied on the cost to allocate a direct buffer is relative high (we will dive into detail of it in next blog);
Ref
Written with StackEdit.
评论
发表评论