Netty的传输API-Netty笔记(四)

/ 0评 / 1

传输API

传输 API 的核心是 Channel 接口,它被用于所有的 I/O 操作。Channel 实现是线程安全的,即使你在多个线程中使用它向远程节点写数据。 Channel 类的层次结构如下:

每个 Channel 都会被分配给一个 ChannelPipeline 和 ChannelConfig 。ChannelConfig 包含了该 Channel 的所有配置,并支持热更新。由于特定的传输可能具有独特的设置,所以它可能会实现一个 ChannelConfig 的子类型。

ChannelPipeline 持有所有将应用于入站和出站数据以及事件的 ChannelHandler 实例,这些 ChannelHandler 实现了应用程序用于处理状态变化以及数据处理的逻辑。

ChannelHandler 的典型用途包括:

ChannelPipeline 实现了一种常见的设计模式拦截过滤器(Intercepting Filter)。UNIX 管道是另外一个熟悉的例子:多个命令被链接在一起,其中一个命令的输出端将连接到命令行中下一个命令的输入端。

你也可以根据需要通过或者移除 ChannelHandler 实例来修改 ChannelPipeline 。通过利用 Netty 的这项能力可以构建出高度灵活的应用程序。例如:每当 STARTTLS 协议被请求时,你可以简单地通过向 ChannelPipeline 添加一个适当的 ChannelHandler (SslHandler)来按需地支持 STARTTLS 协议。

Channel 除了访问所分配的 ChannelPipeline 和 ChannelConfig 之外,还有其他一些常用方法:

内置的传输

Netty 内置了一些可以开箱即用的传输。因为并不是它们所有的传输都支持每一种协议,所以你必须选择一个和你的应用程序所使用的协议相容的传输。

NIO - 非阻塞 I/O

NIO 提供了一个所有 I/O 操作的全异步的实现。它利用了自 NIO 子系统被引入 JDK 1.4 时便可用的基于选择器的 API 。

选择器背后的基本概念是充当一个注册表,在那里你将可用请求在 Channel 的状态发生变化时得到通知。可能的状态变化有:

选择器运行在一个检查状态变化并对其做出响应的线程上,在应用程序对状态的改变做出响应之后,选择器将会被重置,并将重复这个过程。

java.nio.channels.SelectionKey 类定义了一些位模式,可以组合起来定义一组应用程序正在请求通知的状态变化集。

选择并处理状态的变化

零拷贝

零拷贝(zero-copy)是一种目前只有在使用 NIO 和 Epoll 传输时才可以使用的特性。它使你可以快速高效地将数据从文件系统移动到网络接口,而不需要将其从内核空间复制到用户空间,其在像 FTP 或者 HTTP 这样的协议中可以显著地提升性能。但是,并不是所有的操作系统都支持这一特性。特别地,它对于实现了数据加密或压缩的文件系统是不可用的,只能传输文件的原始内容。反过来说,传输已被加密的文件则不是问题。

Epoll - 用于 Linux 的本地非阻塞传输

Netty 为 Linux 提供了一组 NIO API ,其以一种和它本身的设计更加一致的方式使用 epoll ,并且以一种更加轻量的方式使用中断。如果你的应用程序旨在运行于 Linux 系统,那么请考虑利用这个版本的传输。在高负载下它的性能要优于 JDK 的 NIO 实现

如果想使用 epoll 替代 NIO ,只需要在引导服务器的时候,将 NioEventLoopGroup 替换为 EpollEventLoopGroup ,并且将 NioServerSocketChannel.class 替换为 EpollServerSocketChannel.class 。参考下面(示例原地址: https://blog.6ag.cn/2496.html)的示例:

public class EchoServer {

    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public void start() throws Exception {
        final EchoServerHandler serverHandler = new EchoServerHandler();
        EventLoopGroup group = new EpollEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(group)
                    // 指定所使用的 Epoll 传输 Channel
                    .channel(EpollServerSocketChannel.class)
                    // 使用指定的端口设置套接字地址
                    .localAddress(new InetSocketAddress(port))
                    // 添加一个 EchoServerHandler 到子 Channel 的 ChannelPipeline
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            // EchoServerHandler 被标注为 @Sharable,所以我们可以总是使用同样的实例
                            ch.pipeline().addLast(serverHandler);
                        }
                    });
            // 异步地绑定服务器,调用 sync() 方法阻塞等待直到绑定完成
            ChannelFuture future = bootstrap.bind().sync();
            // 获取 Channel 的 CloseFuture,并且阻塞当前线程直到它完成
            future.channel().closeFuture().sync();
        } finally {
            // 关闭 EventLoopGroup ,释放所有的资源
            group.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws Exception {
        new EchoServer(8888).start();
    }
}

用于 JVM 内部通信的 Local 传输

Local 传输用于在同一个 JVM 中运行的客户端和服务器程序之间的异步通信。 这个传输中,和服务器 Channel 相关联的 SocketAddress 并没有绑定物理网络地址。所以这个传输并不接受真正的网络流量,并且不能喝其他传输实现进行互操作,客户端希望连接到(在同一 JVM 中)使用了这个传输的服务器端时也必须使用它。

OIO 的处理逻辑

Embedded 传输

Embedded 传输可以将一组 ChannelHandler 作为帮助器类嵌入到其他的 ChannelHandler 内部,实现扩展一个 ChannelHandler 的功能,而不需要修改其内部代码。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注