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;
syncreturn aFuture<V>,getreturnV, sosynccan 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.
评论
发表评论