Netty

1 Netty设计特点

  1. 是一个由事件驱动驱动的异步网络应用程序框架
  2. 针对多种传输类型的统一接口 - 阻塞和非阻塞
  3. 高度可定制化线程模型——单线程,一个或多个线程池,实现了Reactor Pattern
  4. 支持真实的无连接报文通信
  5. Chaining of logic components to support reuse(逻辑链使得重用更加容易)

2 Netty性能

  1. 比核心 Java API 更好的吞吐量,较低的延时
  2. 资源消耗更少,这个得益于共享池和重用
  3. 减少内存拷贝 (Zero-Copy),Netty的Zero-Copy有2中含义
    1. 和OS中的Zero-Copy含义一样,byte copy是通过DMA来处理的。
    2. 另一种含义的通信层间的Zero-Copy,If data is not modofied a slice can be passed forward without copying to a different buffer,通过slice创建virtual buffer。

Netty线程模型

  1. Netty 4 handes everything that occurs in a given EventLoop in the same thread. This provides a simpler execution architecture and eliminates the need for synchronization in the ChannelHandlers (except for any that might be shared among multiple Channels).
  2. Netty4 a single EventLoop can service multiple Channels; A given EventLoop has one associated thread, not sure one thread can service multiple EventLoop?
  3. Netty如何保证一个Channel相关的所有event都在一个EventLoop上执行? Netty线程模型的高性能取决于对当前执行线程身份的检查:也就是说检查当前执行的线程是否是分配给EventLoop的线程。如果当前执行的线程是分配给EventLoop的线程,将立即执行调用的代码快;否则,把要执行的逻辑放入一个将来执行的任务中,放入和EventLoop相关的内部队列中,When the EventLoop next processes its events, it will execute those in the queue. This explains how any Thread can interact directly with the Channel without requiring synchronization in the ChannelHandlers
  4. 这种线程模型的缺点:阻塞调用或运行时间长的任务将阻塞同一个EventLoop中其他任务的执行。如果有这种任务,可放入专用的EventExecutor执行。


串行化设计避免线程竞争和行线程上下文的切换:我们知道当系统在运行过程中,如果频繁的进行线程上下文切换,会带来额外的性能损耗。多线程并发执行某个业务流程,业务开发者还需要时刻对线程安全保持警惕,哪些数据可能会被并发修改,如何保护?这不仅降低了开发效率,也会带来额外的性能损耗。

为了解决上述问题,Netty采用了串行化设计理念,从消息的读取、编码以及后续Handler的执行,始终都由IO线程NioEventLoop负责,这就意外着整个流程不会进行线程上下文的切换,数据也不会面临被并发修改的风险。

  1. 如果复杂业务放到业务池里处理,怎么对客户端进行返回?

    将Chanel传递到业务线程池中,然后调用Chanel的write方法即可。或:业务线程调用ChannelHandlerContext.write(Object msg)方法进行消息发送

  2. 从IO线程到业务线程?

PipeLine: ChannelPipeline

  1. work 线程是核心分发线程,不负责处理业务逻辑,只是起到分发请求到handler 的作用,默认work线程数你的cpu的核心个数,设置多了没有用,提高并发需要在handler 里面做线程池处理,不然的话 work 线程只能串行处理请求,并发量大减。

Netty的零拷贝(zero copy)

Netty的“零拷贝”主要体现在如下三个方面:

  • Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
  • Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。
  • Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。

    How to fix java.net.SocketException: Broken pipe

  • I was not draining the receiving buffer (the one the source channel reads into) and it eventually got full.

  • Now that it is full, pipeSourceChannel.read(readBuffer) returns 0 bytes. There is data to be read but It can't read on a full buffer.

  • That caused the channel to be closed (i was doing that myself on bytesRead == 0) and the BrokenPipe.

    One lesson I learned here: PIPES are tricky. I would think that non-blocking concurrent queues are much simpler to use like this guy here once mentioned:Java NIO Pipe vs BlockingQueue

Netty: when does writeAndFlush channel future listener callback get executed?

final ChannelFuture writeFuture = abacaChannel.writeAndFlush("Test");
writeFuture.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
        if (writeFuture.isSuccess()) {
            LOGGER.debug("Write successful");
        } else {
            LOGGER.error("Error writing message to Abaca host");
        }
    }
});
After netty hands over the data to the OS send buffers (or)
Listener will be notified after data is removed from ChannelOutboundBuffer (netty's send buffer)

the future is notified once the syscall success. This not means that it was transmitted and received by the remote peer. For this you will need to implement some kind of acknowledge into your protocol.

channelWritabilityChanged

Invoked when the writability state of the Channel changes. The user can ensure writes are not done too quickly (to avoid an

OutOfMemoryError) or can resume writes when the Channel becomes writable again. The Channel method isWritable() can be called to detect the writability of the channel. The threshold for writability can be set via

Channel.config().setWriteHighWaterMark() and

Channel.config().setWriteLowWaterMark().

results matching ""

    No results matching ""