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 | int | 30000 | |
WRITE_SPIN_COUNT | int | 16 | |
ALLOCATOR | ByteBufAllocator | PooledByteBufAllocator.DEFAULT | |
RCVBUF_ALLOCATOR | RecvByteBufAllocator | 无 | |
AUTO_READ | boolean | true | |
AUTO_CLOSE | boolean | true | |
WRITE_BUFFER_WATER_MARK | WriteBufferWaterMark | WriteBufferWaterMark.DEFAULT | |
MESSAGE_SIZE_ESTIMATOR | MessageSizeEstimator | DefaultMessageSizeEstimator. DEFAULT | |
SINGLE_EVENTEXECUTOR_ PER_GROUP | boolean | true | |
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 | int | 0 | |
io.netty.maxThreadLocalCharBufferSize | int | 16K | |
io.netty.allocator.directMemoryCacheAlignment | int | 0 | |
io.netty.allocator.pageSize | int | 8192 | |
io.netty.allocator.maxOrder | int | 9 | |
io.netty.allocator.numHeapArenas | int | 0-cpu*2 | |
io.netty.allocator.numDirectArenas | int | 0-cpu*2 | |
io.netty.allocator.smallCacheSize | int | 256 | |
io.netty.allocator.normalCacheSize | int | 64 | |
io.netty.allocator.maxCachedBufferCapacity | int | 32K | |
io.netty.allocator.cacheTrimInterval | int | 8192 | |
io.netty.allocation.cacheTrimIntervalMillis | int | 0 | |
io.netty.allocator.cacheTrimIntervalMillis | long | 0 | |
io.netty.allocator.useCacheForAllThreads | boolean | false | |
io.netty.allocator.maxCachedByteBuffersPerChunk | int | 1023 | |
io.netty.defaultPromise.maxListenerStackDepth | int | 8 | |
io.netty.globalEventExecutor.quietPeriodSeconds | int | 1 | |
io.netty.eventExecutor.maxPendingTasks | int | Integer. MAX_VALUE | SingleThreadEventExecutor的默认maxPendingTasks |
io.netty.threadLocalMap.stringBuilder.initialSize | int | 1024 | |
io.netty.threadLocalMap.stringBuilder.maxSize | int | 4K | |
io.netty.native.workdir | String | 无 | |
io.netty.native.deleteLibAfterLoading | boolean | true | |
io.netty.native.tryPatchShadedId | boolean | true | |
io.netty.native.detectNativeLibraryDuplicates | boolean | true | |
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 | int | 1024 | |
io.netty.noPreferDirect | boolean | false | 是否不使用直接内存 |
io.netty.osClassifiers | String | 无 | |
user.name | String | 根据系统 | |
java.vm.name | String | 空字符 | |
io.netty.tmpdir | String | 空字符 | |
java.io.tmpdir | String | 空字符 | |
io.netty.bitMode | int | 0 | |
sun.arch.data.model | int | 0 | |
com.ibm.vm.bitmode | int | 0 | |
org.graalvm.nativeimage.imagecode | String | 无 | |
io.netty.noUnsafe | boolean | false | |
io.netty.tryUnsafe | boolean | true | |
org.jboss.netty.tryUnsafe | boolean | true | |
io.netty.tryReflectionSetAccessible | boolean | javaVersion() < 9 | |
io.netty.initialSeedUniquifier | long | 0 | |
java.util.secureRandomSeed | boolean | false | |
io.netty.availableProcessors | int | cpu线程数 | |
io.netty.recycler.maxCapacityPerThread | int | 4096 | |
io.netty.recycler.maxCapacity | int | 4096 | |
io.netty.recycler.chunkSize | int | 32 | |
io.netty.recycler.ratio | int | 8 | |
io.netty.recycler.blocking | boolean | false | |
io.netty.recycler.batchFastThreadLocalOnly | boolean | true | |
io.netty.customResourceLeakDetector | String | 无 | |
io.netty.serviceThreadPrefix | String | 无 | |
io.netty.hostsFileRefreshInterval | long | 0 | |
io.netty.noKeySetOptimization | boolean | false | |
io.netty.selectorAutoRebuildThreshold | int | 512 | |
io.netty.transport.estimateSizeOnSubmit | boolean | true | |
io.netty.transport.writeTaskSizeOverhead | int | 32 | |
io.netty.transport.outboundBufferEntrySizeOverhead | int | 96 | |
io.netty.processId | String | 无 | |
io.netty.machineId | String | 无 | |
io.netty.eventLoopThreads | int | cpu*2 | MultithreadEventLoopGroup的默认线程数 |
io.netty.transport.pendingWriteSizeOverhead | int | 64 | |
io.netty.eventLoop.maxPendingTasks | int | Integer. MAX_VALUE | SingleThreadEventLoop 的默认maxPendingTasks |
io.netty.buffer.bytebuf.checkAccessible | boolean | true | |
io.netty.buffer.checkBounds | boolean | true | |
io.netty.leakDetection.acquireAndReleaseOnly | boolean | false |