Netty使用笔记

Netty中的Channel是I/O操作的核心,支持异步操作,有状态管理和层次结构。ChannelHandler处理I/O事件,分为入站和出站类型。ChannelPipeline实现事件拦截过滤,事件在处理程序之间按特定顺序流转。EventLoopGroup是事件循环组,用于注册和处理Channel。
摘要由CSDN通过智能技术生成

Channel

  功能

the current state of the channel (e.g. is it open? is it connected?),
the configuration parameters of the channel (e.g. receive buffer size),
the I/O operations that the channel supports (e.g. read, write, connect, and bind), and
the ChannelPipeline which handles all I/O events and requestsassociated with the channel.

  异步

Netty中的所有I/O操作都是异步的。这意味着任何I/O调用都将立即返回,并且不能保证在调用结束时已完成请求的I/O操作。相反,将返回一个ChannelFuture实例,该实例将在请求的I/O操作成功、失败或取消时通知您。

  分层

通道可以有一个父级,这取决于它是如何创建的。例如,ServerSocketChannel接受的SocketChannel将在parent()上返回ServerSocketChannelas其父级。

层次结构的语义取决于通道所属的传输实现。例如,您可以编写一个新的通道实现,创建共享一个套接字连接的子通道,就像BEEP和SSH所做的那样。

  向下转型

某些传输会暴露特定于传输的附加操作。将Channel向下转换为子类型以调用此类操作。例如,对于旧的I/O数据报传输,多播加入/离开操作由DatagramChannel提供。

  释放资源

调用close()或close(ChannelPromise)以在使用完Channel后释放所有资源是很重要的。这样可以确保以正确的方式释放所有资源,即文件句柄。

ChannelHandler

处理I/O事件或拦截I/O操作,并将其转发给下一个由ChannelPipeline初始化的Handler。

  子类型

ChannelHandler本身不提供很多方法,但通常必须实现其子类型之一:

•ChannelInboundHandler,用于处理入站I/O事件,

•ChannelOutboundHandler,用于处理出站I/O操作。


或者,为了方便起见,提供了以下适配器类:

•ChannelInboundHandlerAdapter,用于处理入站I/O事件,

•ChannelOutboundHandlerAdapter,用于处理出站I/O操作,以及

•处理入站和出站事件的ChannelDuplexHandler


有关详细信息,请参阅每个子类型的文档。

  上下文对象

ChannelHandler提供了一个ChannelHandlerContext对象。ChannelHandler应该通过上下文对象与其所属的ChannelPipeline进行交互。使用上下文对象,ChannelHandler可以向上游或下游传递事件,动态修改pipeline,或存储handler相关信息(使用AttributeKeys)。

  状态管理

ChannelHandler通常需要存储一些有状态的信息。最简单和推荐的方法是使用成员变量,例如:

 public class DataServerHandler extends SimpleChannelInboundHandler<Message> {

     private boolean loggedIn;

     @Override
     public void channelRead0(ChannelHandlerContext ctx, Message message) {
         if (message instanceof LoginMessage) {
             authenticate((LoginMessage) message);
             loggedIn = true;
         } else (message instanceof GetDataMessage) {
             if (loggedIn) {
                 ctx.writeAndFlush(fetchSecret((GetDataMessage) message));
             } else {
                 fail();
             }
         }
     }
     ...
 }

因为Handler实例有一个专用于一个连接的状态变量,所以必须为每个新Channel创建一个新的Handler实例,以避免出现未经身份验证的客户端可能出现的争用情况。

 // Create a new handler instance per channel.
 // See ChannelInitializer.initChannel(Channel).
 public class DataServerInitializer extends ChannelInitializer<Channel> {
     @Override
     public void initChannel(Channel channel) {
         channel.pipeline().addLast("handler", new DataServerHandler());
     }
 }

  使用AttributeKeys

尽管建议使用成员变量来存储Handler的状态,但由于某些原因,可能不想创建许多Handler实例。在这种情况下,您可以使用ChannelHandlerContext提供的AttributeKeys。


 @Sharable
 public class DataServerHandler extends SimpleChannelInboundHandler<Message> {
     private final AttributeKey<Boolean> auth =
           AttributeKey.valueOf("auth");

     @Override
     public void channelRead(ChannelHandlerContext ctx, Message message) {
         Attribute<Boolean> attr = ctx.attr(auth);
         if (message instanceof LoginMessage) {
             authenticate((LoginMessage) o);
             attr.set(true);
         } else (message instanceof GetDataMessage) {
             if (Boolean.TRUE.equals(attr.get())) {
                 ctx.writeAndFlush(fetchSecret((GetDataMessage) o));
             } else {
                 fail();
             }
         }
     }
     ...
 }

此时Handler实例可以共享

 public class DataServerInitializer extends ChannelInitializer<Channel> {

     private static final DataServerHandler SHARED = new DataServerHandler();

     @Override
     public void initChannel(Channel channel) {
         channel.pipeline().addLast("handler", SHARED);
     }
 }

  @Sharable

如果ChannelHandler使用@Sharable进行了注释,这意味着可以只创建一次实例,并在没有竞争条件的情况下多次将其添加到一个或多个ChannelPipelines。

如果未指定此注释,则每次将其添加到管道时都必须创建一个新的handler实例,因为它具有非共享状态,例如成员变量。

ChannelHandlerContext

使ChannelHandler能够与其ChannelPipeline和其他Handler进行交互。可以通知ChannelPipeline中的下一个ChannelHandler,并动态修改它所属的ChannelPipeline。

  Notify

可以通过调用ctx提供的各种方法之一来通知同一ChannelPipeline中最近的Handler。

  修改pipeline

可以通过调用pipeline()来获取Channel所属的ChannelPipeline。一个不一般的应用程序可以在运行时动态地在pipeline insert, remove, or replace handlers。

  保存引用

您可以保留ChannelHandlerContext以备日后使用,例如将事件跨接在处理程序方法之外,甚至是来自不同线程的事件。

 public class MyHandler extends ChannelDuplexHandler {

     private ChannelHandlerContext ctx;

     public void beforeAdd(ChannelHandlerContext ctx) {
         this.ctx = ctx;
     }

     public void login(String username, password) {
         ctx.write(new LoginMessage(username, password));
     }
     ...
 }

  一对多

请注意,如果ChannelHandler是共享的,则不同的ChannelHandlerContext会持有相同的ChannelHandler。

ChannelPipeline

ChannelPipeline拥有很多Channel。ChannelPipeline实现了拦截过滤器模式的高级形式,使用户能够完全控制事件的处理方式以及pipeline中的ChannelHandlers之间的交互方式。

  创建pipeline

每个Channel都有自己的pipeline,并且在创建新Channel时会自动创建pipeline。

  事件在pipeline的流转

下图描述了ChannelPipeline中的ChannelHandlers通常如何处理I/O事件。

I/O事件由ChannelInboundHandler或ChannelOutboundHandler处理,并通过调用ChannelHandlerContext中定义的事件传播方法(如ChannelHandlerAntext.fireChannelRead(Object)和ChannelHandleerContext.write(Object))转发到其最近的ChannelHandler。

                                                 I/O Request
                                            via Channel or
                                        ChannelHandlerContext
                                                      |
  +---------------------------------------------------+---------------+
  |                           ChannelPipeline         |               |
  |                                                  \|/              |
  |    +---------------------+            +-----------+----------+    |
  |    | Inbound Handler  N  |            | Outbound Handler  1  |    |
  |    +----------+----------+            +-----------+----------+    |
  |              /|\                                  |               |
  |               |                                  \|/              |
  |    +----------+----------+            +-----------+----------+    |
  |    | Inbound Handler N-1 |            | Outbound Handler  2  |    |
  |    +----------+----------+            +-----------+----------+    |
  |              /|\                                  .               |
  |               .                                   .               |
  | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
  |        [ method call]                       [method call]         |
  |               .                                   .               |
  |               .                                  \|/              |
  |    +----------+----------+            +-----------+----------+    |
  |    | Inbound Handler  2  |            | Outbound Handler M-1 |    |
  |    +----------+----------+            +-----------+----------+    |
  |              /|\                                  |               |
  |               |                                  \|/              |
  |    +----------+----------+            +-----------+----------+    |
  |    | Inbound Handler  1  |            | Outbound Handler  M  |    |
  |    +----------+----------+            +-----------+----------+    |
  |              /|\                                  |               |
  +---------------+-----------------------------------+---------------+
                  |                                  \|/
  +---------------+-----------------------------------+---------------+
  |               |                                   |               |
  |       [ Socket.read() ]                    [ Socket.write() ]     |
  |                                                                   |
  |  Netty Internal I/O Threads (Transport Implementation)            |
  +-------------------------------------------------------------------+
 

入站事件由Inbound Handlers按自下而上的方向处理,如图左侧所示。入站处理程序通常处理由图底部的I/O线程生成的入站数据。入站数据通常是通过实际的输入操作(如SocketChannel.read(ByteBuffer))从远程对等端读取的。如果入站事件超出了顶部入站Handler,则会被丢弃,如果需要引起注意可以使用log。

出站事件由Outbound Handlers按自上而下的方向处理,如图右侧所示。出站处理程序通常生成或转换出站流量,如写入请求。如果出站事件超出底部出站处理程,则由与通道关联的I/O线程处理。I/O线程通常执行实际的输出操作,例如SocketChannel.write(ByteBuffer)。

假设我们创建了这样一个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());

在上面的例子中,名称以Inbound开头的类表示它是一个入站处理程序。名称以Outbound开始的类表示是一个出站处理程序。

在给定的示例配置中,当事件进入时,处理程序的评估顺序为1,2,3,4,5。当事件出去时,顺序为5,4,3,2,1。在此原则之上,ChannelPipeline跳过对某些处理程序的评估,以缩短堆栈深度:

•3和4没有实现ChannelInboundHandler,因此inbound事件的实际处理顺序为:1、2和5。

•1和2没有实现ChannelOutboundHandler,因此outbound事件的实际处理顺序为:5、4和3。

•如果5同时实现ChannelInboundHandler和ChannelOutboundHandler,则入站和出站事件的评估顺序可能分别为125和543。

  事件转发

如图所示,处理程序必须调用ChannelHandlerContext中的事件传播方法,才能将事件转发到下一个处理程序。

  • 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:
    • ChannelHandlerContext.bind(SocketAddress, ChannelPromise)
    • ChannelHandlerContext.connect(SocketAddress, SocketAddress, ChannelPromise)
    • ChannelHandlerContext.write(Object, ChannelPromise)
    • ChannelHandlerContext.flush()
    • ChannelHandlerContext.read()
    • ChannelHandlerContext.disconnect(ChannelPromise)
    • ChannelHandlerContext.close(ChannelPromise)
    • ChannelHandlerContext.deregister(ChannelPromise)

下面的示例显示了事件传播通常是如何进行的:

 public class MyInboundHandler extends ChannelInboundHandlerAdapter {
     @Override
     public void channelActive(ChannelHandlerContext ctx) {
         System.out.println("Connected!");
         ctx.fireChannelActive();
     }
 }

 public class MyOutboundHandler extends ChannelOutboundHandlerAdapter {
     @Override
     public void close(ChannelHandlerContext ctx, ChannelPromise promise) {
         System.out.println("Closing ..");
         ctx.close(promise);
     }
 }

  构建pipeline

用户应该在pipeline中有一个或多个ChannelHandler来接收I/O事件(例如读取)和请求I/O操作(例如写入和关闭)。例如您的程序可能会因协议和业务逻辑:

1.协议解码器-将二进制数据(例如ByteBuf)转换为Java对象。

2.协议编码器-将Java对象转换为二进制数据。

3.业务逻辑处理程序-执行实际的业务逻辑(例如数据库访问)。

可以表示为如下示例:

 static final 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
 // 在与I/O线程不同的线程中,这样I/O线程就不会被耗时的任务阻塞。
 // 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());

请注意,虽然使用DefaultEventLoopGroup,但它仍将按照ChannelHandlerContext以串行方式处理任务,因此保证顺序。由于顺序的原因,这仍然可能成为一个瓶颈。如果您的用例不需要顺序,您可能需要考虑使用UnorderedThreadPoolEventExecutor来最大限度地提高任务执行的并行性。

  线程安全

可以随时添加或删除ChannelHandler,因为ChannelPipeline是线程安全的。For example, you can insert an encryption handler when sensitive information is about to be exchanged, and remove it after the exchange.

ChannelInitializer

继承ChannelInboundHandlerAdapter。

他的initChannel方法会在Channel注册成功时执行,执行后该实例会从ChannelPipeline移除。

ChannelFuture

异步通道I/O操作的结果。

Netty中的所有I/O操作都是异步的。这意味着任何I/O调用都将立即返回,并且不能保证在调用结束时已完成请求的I/O操作。相反,您将返回一个ChannelFuture实例,该实例为您提供有关I/O操作的结果或状态的信息。

ChannelFuture未完成或已完成。当I/O操作开始时,将创建一个新的future对象。新的未来最初是未完成的——它既没有成功,也没有失败,也没有因为I/O操作尚未完成而被取消。如果I/O操作成功、失败或取消,则未来将标记为已完成,并包含更具体的信息,例如故障原因。请注意,即使失败和取消也属于完整状态。

                                      +---------------------------+
                                      | Completed successfully    |
                                      +---------------------------+
                                 +---->      isDone() = true      |
 +--------------------------+    |    |   isSuccess() = true      |
 |        Uncompleted       |    |    +===========================+
 +--------------------------+    |    | Completed with failure    |
 |      isDone() = false    |    |    +---------------------------+
 |   isSuccess() = false    |----+---->      isDone() = true      |
 | isCancelled() = false    |    |    |       cause() = non-null  |
 |       cause() = null     |    |    +===========================+
 +--------------------------+    |    | Completed by cancellation |
                                 |    +---------------------------+
                                 +---->      isDone() = true      |
                                      | isCancelled() = true      |
                                      +---------------------------+

提供了各种方法来检查I/O操作是否已完成,等待完成,并检索I/O操作的结果。它还允许您添加ChannelFutureListeners,以便在I/O操作完成时收到通知。

  首选addListener

建议尽可能选择addListener(GenericFutureListener)而不是await(),以便在I/O操作停止时得到通知并执行任何后续任务。

addListener(GenericFutureListener)是非阻塞的。它只需将指定的ChannelFutureListener添加到ChannelFuture中,I/O线程将在与该future相关联的I/O操作完成时通知侦听器。ChannelFutureListener产生了最好的性能和资源利用率,因为它根本不阻塞,但如果你不习惯事件驱动的编程,实现顺序逻辑可能会很棘手。

相比之下,await()是一个阻塞操作。调用后,调用程序线程将阻塞,直到操作完成。使用await()实现顺序逻辑更容易,但调用方线程必须阻塞,直到I/O操作完成,并且线程间通知的成本相对较低。此外,在特定的情况下,也有可能发生死锁,如下所述。

  不要在ChannelHandler内部调用await

ChannelHandler中的事件处理程序方法通常由I/O线程调用。如果await()由I/O线程调用的事件处理程序方法调用,则它正在等待的I/O操作可能永远不会完成。

 // BAD - NEVER DO THIS
 @Override
 public void channelRead(ChannelHandlerContext ctx, Object msg) {
     ChannelFuture future = ctx.channel().close();
     future.awaitUninterruptibly();
     // Perform post-closure operation
     // ...
 }

 // GOOD
 @Override
 public void channelRead(ChannelHandlerContext ctx, Object msg) {
     ChannelFuture future = ctx.channel().close();
     future.addListener(new ChannelFutureListener() {
         public void operationComplete(ChannelFuture future) {
             // Perform post-closure operation
             // ...
         }
     });
 }
 

尽管存在上述缺点,但在某些情况下,调用await()更方便。在这种情况下,请确保不要在I/O线程中调用await()。否则,将引发BlockingOperationException以防止死锁。

  不要混淆I/O超时和等待超时

您使用await(long), await(long, TimeUnit), awaitUninterruptibly(long), or awaitUninterruptibly(long, TimeUnit)指定的超时值与I/Otimeout完全无关。如果I/O操作超时,则未来将标记为“已完成但出现故障”,如上图所示。例如,应通过特定于传输的选项配置连接超时:

 // BAD - NEVER DO THIS
 Bootstrap b = ...;
 ChannelFuture f = b.connect(...);
 f.awaitUninterruptibly(10, TimeUnit.SECONDS);
 if (f.isCancelled()) {
     // Connection attempt cancelled by user
 } else if (!f.isSuccess()) {
     // You might get a NullPointerException here because the future
     // might not be completed yet.
     f.cause().printStackTrace();
 } else {
     // Connection established successfully
 }

 // GOOD
 Bootstrap b = ...;
 // Configure the connect timeout option.
 b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
 ChannelFuture f = b.connect(...);
 f.awaitUninterruptibly();

 // Now we are sure the future is completed.
 assert f.isDone();

 if (f.isCancelled()) {
     // Connection attempt cancelled by user
 } else if (!f.isSuccess()) {
     f.cause().printStackTrace();
 } else {
     // Connection established successfully
 }

ChannelFutureListener

监听ChannelFuture的结果。一旦通过调用ChannelFuture.addListener(GenericFutureListener)添加此listener,就会通知同步通道I/O操作的结果。

  性能相关

operationComplete(Future)由I/Othread直接调用。因此,在处理程序方法中执行耗时的任务或阻塞操作可能会导致I/O期间出现意外的暂停。如果您需要在I/O完成时执行阻塞操作,请尝试使用线程池在其他线程中执行该操作。

EventLoopGroup

一种特殊的EventExecutorGroup,在他上面可以注册Channel,以便在后续的selection  event loop时得到处理。

  MultithreadEventLoopGroup

多线程的抽象子类,具体实现类有NioEventLoopGroup、DefaultEventLoopGroup(用于local transport)

  EventLoop和SingleThreadEventLoop

单线程的子接口、抽象子类,具体实现类有NioEventLoop、DefaultEventLoop(用于local transport)

参数配置

  ChannelOptions

DefaultChannelConfig类型默认值描述

CONNECT_TIMEOUT_MILLIS

int30000

WRITE_SPIN_COUNT

int

16

ALLOCATOR

ByteBufAllocator

PooledByteBufAllocator.DEFAULT

RCVBUF_ALLOCATOR

RecvByteBufAllocator

AUTO_READ

booleantrue

AUTO_CLOSE

booleantrue

WRITE_BUFFER_WATER_MARK

WriteBufferWaterMark

WriteBufferWaterMark.DEFAULT

MESSAGE_SIZE_ESTIMATOR

MessageSizeEstimator

DefaultMessageSizeEstimator.

DEFAULT

SINGLE_EVENTEXECUTOR_

PER_GROUP

booleantrue

MAX_MESSAGES_PER_WRITE

int

Integer.MAX_VALUE

DefaultServerSocketChannelConfig

类型默认值描述

SO_RCVBUF

int未知

调用JDK的

ServerSocket

.setReceiveBufferSize(i)

SO_REUSEADDR

boolean未知

调用JDK的

ServerSocket

.setReuseAddress(b)

SO_BACKLOG

int未知

NioServerSocketChannelConfig

设置JDK的ServerSocketChannel参数

DefaultSocketChannelConfig

包括Server端的Client

类型默认值描述

SO_RCVBUF

int未知

调用JDK的

ServerSocket

.setReceiveBufferSize(i)

SO_SNDBUF

int未知

调用JDK的

ServerSocket

.setSendBufferSize(i)

TCP_NODELAY

boolean未知

调用JDK的

ServerSocket

.setTcpNoDelay(b)

SO_KEEPALIVE

boolean未知

调用JDK的

ServerSocket

.setKeepAlive(b)

SO_REUSEADDR

boolean未知

调用JDK的

ServerSocket

.setReuseAddress(b)

SO_LINGER

int未知

调用JDK的

ServerSocket

.setSoLinger(i)

IP_TOS

int未知

调用JDK的

ServerSocket

.setTrafficClass(i)

ALLOW_HALF_CLOSURE

boolean

NioSocketChannelConfig

包括Server端的Client

设置JDK的ServerSocketChannel参数

其他不再详列,参考对应Channel的Config类进行配置

  系统参数 

参数名称类型默认值描述

io.netty.allocator.type

String

pooled

Bytebuf的类型

可选unpooled

pooled

io.netty.threadLocalDirectBufferSize

int0

io.netty.maxThreadLocalCharBufferSize

int

16K

io.netty.allocator.directMemoryCacheAlignment

int0

io.netty.allocator.pageSize

int8192

io.netty.allocator.maxOrder

int9

io.netty.allocator.numHeapArenas

int

0-cpu*2

io.netty.allocator.numDirectArenas

int0-cpu*2

io.netty.allocator.smallCacheSize

int256

io.netty.allocator.normalCacheSize

int64

io.netty.allocator.maxCachedBufferCapacity

int32K

io.netty.allocator.cacheTrimInterval

int8192

io.netty.allocation.cacheTrimIntervalMillis

int0

io.netty.allocator.cacheTrimIntervalMillis

long0

io.netty.allocator.useCacheForAllThreads

booleanfalse

io.netty.allocator.maxCachedByteBuffersPerChunk

int1023

io.netty.defaultPromise.maxListenerStackDepth

int8

io.netty.globalEventExecutor.quietPeriodSeconds

int1

io.netty.eventExecutor.maxPendingTasks

int

Integer.

MAX_VALUE

SingleThreadEventExecutor的默认maxPendingTasks

io.netty.threadLocalMap.stringBuilder.initialSize

int1024

io.netty.threadLocalMap.stringBuilder.maxSize

int4K

io.netty.native.workdir

String

io.netty.native.deleteLibAfterLoading

booleantrue

io.netty.native.tryPatchShadedId

booleantrue

io.netty.native.detectNativeLibraryDuplicates

booleantrue

io.netty.util.internal.ObjectCleaner.

refQueuePollTimeout

int

10000

os.arch

String空字符

os.name

String空字符

io.netty.maxDirectMemory

long-1

// * < 0 - Don't use cleaner, and inherit max direct memory from java. In this case the

// "practical max direct memory" would be 2 * max memory as defined by the JDK.

// * == 0 - Use cleaner, Netty will not enforce max memory, and instead will defer to JDK.

// * > 0 - Don't use cleaner. This will limit Netty's total direct memory

// (note: that JDK's direct memory limit is independent of this).

io.netty.uninitializedArrayAllocationThreshold

int1024

io.netty.noPreferDirect

booleanfalse是否不使用直接内存

io.netty.osClassifiers

String

user.name

String根据系统

java.vm.name

String空字符

io.netty.tmpdir

String空字符

java.io.tmpdir

String空字符

io.netty.bitMode

int0

sun.arch.data.model

int0

com.ibm.vm.bitmode

int0

org.graalvm.nativeimage.imagecode

String

io.netty.noUnsafe

booleanfalse

io.netty.tryUnsafe

booleantrue

org.jboss.netty.tryUnsafe

booleantrue

io.netty.tryReflectionSetAccessible

boolean

javaVersion() < 9

io.netty.initialSeedUniquifier

long0

java.util.secureRandomSeed

booleanfalse

io.netty.availableProcessors

intcpu线程数

io.netty.recycler.maxCapacityPerThread

int4096

io.netty.recycler.maxCapacity

int4096

io.netty.recycler.chunkSize

int32

io.netty.recycler.ratio

int8

io.netty.recycler.blocking

booleanfalse

io.netty.recycler.batchFastThreadLocalOnly

booleantrue

io.netty.customResourceLeakDetector

String

io.netty.serviceThreadPrefix

String

io.netty.hostsFileRefreshInterval

long0

io.netty.noKeySetOptimization

booleanfalse

io.netty.selectorAutoRebuildThreshold

int512

io.netty.transport.estimateSizeOnSubmit

booleantrue

io.netty.transport.writeTaskSizeOverhead

int32

io.netty.transport.outboundBufferEntrySizeOverhead

int96

io.netty.processId

String

io.netty.machineId

String

io.netty.eventLoopThreads

intcpu*2

MultithreadEventLoopGroup的默认线程数

io.netty.transport.pendingWriteSizeOverhead

int64

io.netty.eventLoop.maxPendingTasks

int

Integer.

MAX_VALUE

SingleThreadEventLoop

的默认maxPendingTasks

io.netty.buffer.bytebuf.checkAccessible

booleantrue

io.netty.buffer.checkBounds

booleantrue

io.netty.leakDetection.acquireAndReleaseOnly

booleanfalse

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值