跳过导航

作为通用库使用

您知道此页面是从 Github Wiki 页面 自动生成的,对吧?您可以在 此处 自行改进!

Netty 是用于构建网络应用程序的框架,但它还提供了一些基础类,即使不执行套接字 I/O 的程序也可以使用,这些类非常方便。

缓冲区 API

io.netty.buffer 提供了一种名为 ByteBuf 的通用缓冲区类型。它类似于 java.nio.ByteBuffer,但速度更快、更易于使用且可扩展。

易用性

您是否曾经忘记调用 java.nio.ByteBuffer.flip(),并想知道为什么缓冲区中不包含任何内容?在 ByteBuf 中永远不会发生这种情况,因为它有两个索引,一个用于读取,另一个用于写入

ByteBuf buf = ...;
buf.writeUnsignedInt(42);
assertThat(buf.readUnsignedInt(), is(42));

它有一组更丰富的访问方法,可以更轻松地访问缓冲区的内容。例如,它具有用于有符号和无符号整数、搜索和字符串的访问器方法。

可扩展性

您无法对 java.nio.ByteBuffer 进行子类化,但您可以对 ByteBuf 进行子类化。还为您提供了抽象骨架实现,以方便您使用。因此,您可以编写自己的缓冲区实现,例如文件支持的、复合的,甚至混合的。

性能

分配新的 java.nio.ByteBuffer 时,其内容将填充为零。此“清零”消耗 CPU 周期和内存带宽。通常,缓冲区会立即从某个数据源填充,因此清零没有任何好处。

要回收,java.nio.ByteBuffer 依赖于 JVM 垃圾收集器。它适用于堆缓冲区,但不适用于直接缓冲区。根据设计,直接缓冲区预计会存在很长时间。因此,分配许多短寿命的直接 NIO 缓冲区通常会导致 OutOfMemoryError。此外,使用(隐藏的专有)API 显式释放直接缓冲区并不是很快。

ByteBuf 的生命周期与其引用计数绑定。当其计数变为零时,其底层内存区域(byte[] 或直接缓冲区)将被显式取消引用、释放或返回到池中。

Netty 还提供了一个可靠的缓冲池实现,它不会浪费 CPU 周期或内存带宽来清零其缓冲区

ByteBufAllocator alloc = PooledByteBufAllocator.DEFAULT;
ByteBuf buf = alloc.directBuffer(1024);
...
buf.release(); // The direct buffer is returned to the pool.

但是,引用计数并不是万能的。如果 JVM 在其底层内存区域返回池之前对池缓冲区进行垃圾回收,则泄漏最终会耗尽池。

为了帮助您解决泄漏问题,Netty 提供了一种泄漏检测机制,它足够灵活,可以权衡应用程序的性能和泄漏报告的详细信息。有关更多信息,请参阅 引用计数对象

可监听的 Future 和事件循环

异步执行任务(计划任务并在完成时得到通知)很常见,而且应该很容易。当java.util.concurrent.Future首次出现时,我们的兴奋并没有持续多久。我们不得不阻塞才能在完成时得到通知。在异步编程中,你指定在完成时做什么,而不是等待结果。

io.netty.concurrent.Future是 JDK Future的子类型。它允许你添加一个侦听器,当 future 完成时,该侦听器将被事件循环调用。

io.netty.util.concurrent.EventExecutor是一个单线程事件循环,它扩展了java.util.concurrent.ScheduledExecutorService。你可以构建自己的事件循环或将其用作功能丰富的任务执行器。通常,你创建多个EventExecutor来利用并行性

EventExecutorGroup group = new DefaultEventExecutorGroup(4); // 4 threads
Future<?> f = group.submit(new Runnable() { ... });
f.addListener(new FutureListener<?> {
  public void operationComplete(Future<?> f) {
    ..
  }
});
...

全局事件循环

有时你想要一个始终可用的唯一执行器,并且不需要生命周期管理。GlobalEventExecutor是一个单线程单例EventExecutor,它会延迟启动其线程,并在一段时间内没有待处理的任务时停止。

GlobalEventExecutor.INSTANCE.execute(new Runnable() { ... });

在内部,Netty 使用它来通知其他EventExecutor的终止。

与平台相关的操作

请注意,此功能仅供内部使用。如果需求足够,我们正在考虑将其移出内部包。

io.netty.util.internal.PlatformDependent提供与平台相关的和潜在不安全的操作。你可以将其视为sun.misc.Unsafe和其他与平台相关的专有 API 的一层薄薄的层。

其他实用程序

为了构建一个高性能的网络应用程序框架,我们引入了实用程序。你可能会发现一些有用的东西。

线程局部对象池

如果你的线程是长时间运行的,并且你分配了许多同类型的短生存期对象,则可以使用称为Recycler的线程局部对象池。它减少了你产生的垃圾数量,从而节省了内存带宽消耗和垃圾收集器的负载。

public class MyObject {

  private static final Recycler<MyObject> RECYCLER = new Recycler<MyObject>() {
    protected MyObject newObject(Recycler.Handle<MyObject> handle) {
      return new MyObject(handle);
    }
  }

  public static MyObject newInstance(int a, String b) {
    MyObject obj = RECYCLER.get();
    obj.myFieldA = a;
    obj.myFieldB = b;
    return obj;
  }
    
  private final Recycler.Handle<MyObject> handle;
  private int myFieldA;
  private String myFieldB;

  private MyObject(Handle<MyObject> handle) {
    this.handle = handle;
  }
  
  public boolean recycle() {
    myFieldA = 0;
    myFieldB = null;
    return handle.recycle(this);
  }
}

MyObject obj = MyObject.newInstance(42, "foo");
...
obj.recycle();

用户可扩展枚举

enum非常适合静态常量集,但你无法扩展它。当需要在运行时添加更多常量或允许第三方定义其他常量时,请改用可扩展的io.netty.util.ConstantPool

public final class Foo extends AbstractConstant<Foo> {
  Foo(int id, String name) {
    super(id, name);
  }
}

public final class MyConstants {

  private static final ConstantPool<Foo> pool = new ConstantPool<Foo>() {
    @Override
    protected Foo newConstant(int id, String name) {
      return new Foo(id, name);
    }
  };

  public static Foo valueOf(String name) {
    return pool.valueOf(name);
  }

  public static final Foo A = valueOf("A");
  public static final Foo B = valueOf("B");
}

private final class YourConstants {
  public static final Foo C = MyConstants.valueOf("C");
  public static final Foo D = MyConstants.valueOf("D");
}

Netty 使用ConstantPool来定义ChannelOption,以便非核心传输可以以类型安全的方式定义特定于传输的选项。

属性映射

使用io.netty.util.AttributeMap接口来快速、类型安全、线程安全地收集键值对

public class Foo extends DefaultAttributeMap {
  ...
}

public static final AttributeKey<String> ATTR_A = AttributeKey.valueOf("A");
public static final AttributeKey<Integer> ATTR_B = AttributeKey.valueOf("B");

Foo o = ...;
o.attr(ATTR_A).set("foo");
o.attr(ATTR_B).set(42);

正如你可能已经注意到的,AttributeKey是一个Constant

哈希轮计时器

哈希轮计时器是java.util.Timerjava.util.concurrent.ScheduledThreadPoolExecutor的可扩展替代方案。它可以高效地处理许多计划任务及其取消,如下表所示

计划新任务 取消任务
HashedWheelTimer O(1) O(1)
java.util.TimerScheduledThreadPoolExecutor O(logN) O(logN),其中 N = 待处理的任务数

在内部,它使用哈希表,其键是任务的计时,从而为大多数计时器操作产生恒定时间。(java.util.Timer 使用二进制堆。)

有关哈希轮计时器的更多信息,请参阅 这些幻灯片(“哈希和分层计时轮”,Dharmapurikar)此论文(“哈希和分层计时轮:用于高效实现计时器工具的数据结构”,Varghese 和 Lauck)

更多杂项实用程序

以下类很有用,但您会在其他库(如 Guava)中找到不错的替代品

  • io.netty.util.CharsetUtil 提供常用的 java.nio.charset.Charset
  • io.netty.util.NetUtil 提供常用的与网络相关的常量,如 IPv4 和 IPv6 环回地址的 InetAddress
  • io.netty.util.DefaultThreadFactory 是一个通用的 ThreadFactory 实现,可让您轻松配置执行器线程。

与 Guava 和 JDK8 的比较

由于 Netty 尝试最大程度地减少其依赖项集,因此其一些实用程序类与其他流行库(如 Guava)中的类类似。

此类库提供各种实用程序类和备用数据类型,以减轻使用 JDK API 的痛苦,并且它们通常做得很好。

Netty 专注于提供以下所需的构造:

  • 异步编程
  • 低级操作(又称“机械同情”),如
    • 堆外访问
    • 访问专有固有操作
    • 与平台相关的行为

Java 有时会通过采用包含 Netty 提供的构造的思想来进步。例如,JDK 8 添加了 CompletableFuture,它在某种程度上与 io.netty.util.concurrent.Future 重叠。在这种情况下,Netty 的构造为您提供了良好的迁移路径;我们会勤勉地更新 API,以考虑未来的迁移。

上次检索时间为 2024 年 7 月 19 日