zoukankan      html  css  js  c++  java
  • Netty专题(四)-----Netty介绍、Netty组件介绍

    Netty是什么?为什么要用Netty?

    介绍

    Netty是由JBOSS提供的一个java开源框架。 Netty提供异步的、事件驱动的网络应用程序框架和工具, 用以快速开发高性能、高可靠性的网络服务器和客户端程序。

    为什么要用Netty

    1、虽然JAVA NIO框架提供了 多路复用IO的支持,但是并没有提供上层“信息格式”的良好封装。例如前两者并没有提供针对 Protocol Buffer、JSON这些信息格式的封装,但是Netty框架提供了这些数据格式封装(基于责任链模式的编码和解码功能);

    2、直接使用NIO需要需要额外的技能,例如Java多线程,网络编程;

    3、要编写一个可靠的、易维护的、高性能的NIO服务器应用。除了框架本身要兼容实现各类操作系统的实现外。更重要的是它应该还要处理很多上层特有服务,例如:客户端的权限、还有上面提到的信息格式封装、简单的数据读取,断连重连,半包读写,心跳等等,这些Netty框架都提供了响应的支持。

    4、JAVA NIO框架存在一个poll/epoll bug:Selector doesn’t block on Selector.select(timeout),不能block意味着CPU的使用率会变成100%(这是底层JNI的问题,上层要处理这个异常实际上也好办)。当然这个bug只有在Linux内核上才能重现。这个问题在JDK 1.7版本中还没有被完全解决,但是Netty已经将这个bug进行了处理。

    这个Bug与操作系统机制有关系的,JDK虽然仅仅是一个兼容各个操作系统平台的软件,但在JDK5和JDK6最初的版本中(严格意义上来将,JDK部分版本都是),这个问题并没有解决,而将这个帽子抛给了操作系统方,这也就是这个bug最终一直到2013年才最终修复的原因(JDK7和JDK8之间)。

    为什么不用Netty5

    1. netty5 中使用了 ForkJoinPool,增加了代码的复杂度,但是对性能的改善却不明显

    2. 多个分支的代码同步工作量很大

    3. 作者觉得当下还不到发布一个新版本的时候

    4. 在发布版本之前,还有更多问题需要调查一下,比如是否应该废弃 exceptionCaught, 是否暴露EventExecutorChooser等等。

    为什么Netty使用NIO而不是AIO?

    Netty不看重Windows上的使用,在Linux系统上,AIO的底层实现仍使用EPOLL,没有很好实现AIO,因此在性能上没有明显的优势,而且被JDK封装了一层不容易深度优化。AIO还有个缺点是接收数据需要预先分配缓存, 而不是NIO那种需要接收时才需要分配缓存, 所以对连接数量非常大但流量小的情况, 内存浪费很多。据说Linux上AIO不够成熟,处理回调结果速度跟不上处理需求,有点像外卖员太少,顾客太多,供不应求,造成处理速度有瓶颈。

    作者原话:

    Not faster than NIO (epoll) on unix systems (which is true)   There is no daragram suppport   Unnecessary threading model (too much abstraction without usage)

    翻译一下:

    在unix系统上不比NIO(epoll)快(这是事实)没有数据包支持不必要的线程模型(过多的抽象而无需使用)

    Hello Netty代码示例 

    1、服务端

    EchoServer 

    package cn.enjoyedu.ch02.echo;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    
    import java.net.InetSocketAddress;
    
    public class EchoServer  {
    
        private final int port;
    
        public EchoServer(int port) {
            this.port = port;
        }
    
        public static void main(String[] args) throws InterruptedException {
            int port = 9999;
            EchoServer echoServer = new EchoServer(port);
            System.out.println("服务器即将启动");
            echoServer.start();
            System.out.println("服务器关闭");
        }
    
        public void start() throws InterruptedException {
            final EchoServerHandler serverHandler = new EchoServerHandler();
            /*线程组*/
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                /*服务端启动必备*/
                ServerBootstrap b = new ServerBootstrap();
                b.group(group)
                        /*指明使用NIO进行网络通讯*/
                        .channel(NioServerSocketChannel.class)
                        /*指明服务器监听端口*/
                        .localAddress(new InetSocketAddress(port))
                        /*接收到连接请求,新启一个socket通信,也就是channel,每个channel
                        * 有自己的事件的handler*/
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                ch.pipeline().addLast(new EchoServerHandler());
                            }
                        });
                /*绑定到端口,阻塞等待直到连接完成*/
                ChannelFuture f = b.bind().sync();
                /*阻塞,直到channel关闭*/
                f.channel().closeFuture().sync();
            } finally {
                group.shutdownGracefully().sync();
            }
        }
    }

    EchoServerHandler

    package cn.enjoyedu.ch02.echo;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelFutureListener;
    import io.netty.channel.ChannelHandler;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import io.netty.util.CharsetUtil;
    
    /**
     * 类说明:自己的业务处理
     */
    /*指明我这个handler可以在多个channel之间共享,意味这个实现必须线程安全的。*/
    @ChannelHandler.Sharable
    public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    
        /*** 服务端读取到网络数据后的处理*/
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf in = (ByteBuf)msg;/*netty实现的缓冲区*/
            System.out.println("Server Accept:"+in.toString(CharsetUtil.UTF_8));
            ctx.write(in);
        }
    
        /*** 服务端读取完成网络数据后的处理*/
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)/*flush掉所有的数据*/
                    .addListener(ChannelFutureListener.CLOSE);/*当flush完成后,关闭连接*/
        }
    
        /*** 发生异常后的处理*/
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    }

    2、客户端

    EchoClient

    package cn.enjoyedu.ch02.echo;
    
    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioSocketChannel;
    
    import java.net.InetSocketAddress;
    
    /**
     * 类说明:netty的客户端
     */
    public class EchoClient {
    
        private final int port;
        private final String host;
    
        public EchoClient(int port, String host) {
            this.port = port;
            this.host = host;
        }
    
        public void start() throws InterruptedException {
            /*线程组*/
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                /*客户端启动必备*/
                Bootstrap b = new Bootstrap();
                b.group(group)
                    /*指明使用NIO进行网络通讯*/
                    .channel(NioSocketChannel.class)
                    /*配置远程服务器的地址*/
                    .remoteAddress(new InetSocketAddress(host,port))
                    .handler(new EchoClientHandler());
                /*连接到远程节点,阻塞等待直到连接完成*/
                ChannelFuture f = b.connect().sync();
                /*阻塞,直到channel关闭*/
                f.channel().closeFuture().sync();
            } finally {
                group.shutdownGracefully().sync();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            new EchoClient(9999,"127.0.0.1").start();
        }
    }

    EchoClientHandler

    package cn.enjoyedu.ch02.echo;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import io.netty.util.CharsetUtil;
    
    public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    
        /*客户端读取到数据后干什么*/
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
            System.out.println("Client accetp:"+msg.toString(CharsetUtil.UTF_8));
        }
    
        /*客户端被通知channel活跃以后,做事*/
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            //往服务器写数据
            ctx.writeAndFlush(Unpooled.copiedBuffer("Hello,Netty",
                    CharsetUtil.UTF_8));
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    
    }

    Netty核心组件

    Channel

    Channel 是Java NIO 的一个基本构造。它代表一个到实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的I/O操作的程序组件)的开放连接,如读操作和写操作。目前,可以把Channel 看作是传入(入站)或者传出(出站)数据的载体。因此,它可以被打开或者被关闭,连接或者断开连接。

    回调和Future

    一个回调其实就是一个方法,一个指向已经被提供给另外一个方法的方法的引用。这使得后者可以在适当的时候调用前者。回调在广泛的编程场景中都有应用,而且也是在操作完成后通知相关方最常见的方式之一。Netty 在内部使用了回调来处理事件;当一个回调被触发时,相关的事件可以被一个interface-ChannelHandler 的实现处理。Future 提供了另一种在操作完成时通知应用程序的方式。这个对象可以看作是一个异步操作的结果的占位符;它将在未来的某个时刻完成,并提供对其结果的访问。

    JDK 预置了interface java.util.concurrent.Future,但是其所提供的实现,只允许手动检查对应的操作是否已经完成,或者一直阻塞直到它完成。这是非常繁琐的,所以Netty提供了它自己的实现——ChannelFuture,用于在执行异步操作的时候使用。

    ChannelFuture提供了几种额外的方法,这些方法使得我们能够注册一个或者多个ChannelFutureListener实例。监听器的回调方法operationComplete(),将会在对应的操作完成时被调用。然后监听器可以判断该操作是成功地完成了还是出错了。如果是后者,我们可以检索产生的Throwable。简而

    言之,由ChannelFutureListener提供的通知机制消除了手动检查对应的操作是否完成的必要。每个Netty 的出站I/O 操作都将返回一个ChannelFuture。

    事件和ChannelHandler

    Netty 使用不同的事件来通知我们状态的改变或者是操作的状态。这使得我们能够基于已经发生的事件来触发适当的动作。Netty事件是按照它们与入站或出站数据流的相关性进行分类的。

    可能由入站数据或者相关的状态更改而触发的事件包括:

    • 1、连接已被激活或者连接失活;
    • 2、数据读取;
    • 3、用户事件;
    • 4、错误事件。

    出站事件是未来将会触发的某个动作的操作结果,这些动作包括:

    • 1、打开或者关闭到远程节点的连接;
    • 2、将数据写到或者冲刷到套接字。

    每个事件都可以被分发给ChannelHandler 类中的某个用户实现的方法。可以认为每个Channel-Handler 的实例都类似于一种为了响应特定事件而被执行的回调。Netty 提供了大量预定义的可以开箱即用的ChannelHandler 实现,包括用于各种协议(如HTTP 和SSL/TLS)的ChannelHandler。

    组件详细介绍

    Channel、EventLoop 和ChannelFuture

    Netty 网络抽象的代表

    Channel—Socket;

    EventLoop—控制流、多线程处理、并发;

    ChannelFuture—异步通知。

    Channel 接口

    基本的I/O 操作(bind()、connect()、read()和write())依赖于底层网络传输所提供的原语。在基于Java 的网络编程中,其基本的构造是class Socket。Netty 的Channel 接口所提供的API,被用于所有的I/O 操作。大大地降低了直接使用Socket 类的复杂性。此外,Channel 也是拥有许多预定义的、

    专门化实现的广泛类层次结构的根。

    每个Channel 都将会被分配一个ChannelPipeline 和ChannelConfig。ChannelConfig 包含了该Channel 的所有配置设置,并且支持热更新。由于Channel 是独一无二的,所以为了保证顺序将Channel 声明为java.lang.Comparable 的一个子接口。因此,如果两个不同的Channel 实例都返回了相同的

    散列码,那么AbstractChannel 中的compareTo()方法的实现将会抛出一个Error。

    Channel 的生命周期状态

    ChannelUnregistered Channel 已经被创建,但还未注册到EventLoop

    ChannelRegistered Channel 已经被注册到了EventLoop

    ChannelActive Channel 处于活动状态(已经连接到它的远程节点)。它现在可以接收和发送数据了

    ChannelInactive Channel 没有连接到远程节点

    最重要Channel 的方法

    eventLoop 返回分配给Channel 的EventLoop

    pipeline 返回分配给Channel 的ChannelPipeline

    isActive 如果Channel 是活动的,则返回true。活动的意义可能依赖于底层的传输。例如,一个Socket 传输一旦连接到了远程节点便是活动的,而一个Datagram 传输一旦被打开便是活动的。

    localAddress 返回本地的SokcetAddress

    remoteAddress 返回远程的SocketAddress

    write 将数据写到远程节点。这个数据将被传递给ChannelPipeline,并且排队直到它被冲刷

    flush 将之前已写的数据冲刷到底层传输,如一个Socket

    writeAndFlush 一个简便的方法,等同于调用write()并接着调用flush()

    EventLoop和EventLoopGroup

    EventLoop 定义了Netty 的核心抽象,用于处理连接的生命周期中所发生的事件。io.netty.util.concurrent 包构建在JDK 的java.util.concurrent 包上。,一个EventLoop 将由一个永远都不会改变的Thread 驱动,同时任务(Runnable 或者Callable)可以直接提交给EventLoop 实现,以立即执行或者

    调度执行。

    根据配置和可用核心的不同,可能会创建多个EventLoop 实例用以优化资源的使用,并且单个EventLoop 可能会被指派用于服务多个Channel。

    Netty的EventLoop在继承了ScheduledExecutorService的同时,只定义了一个方法,parent()。在Netty 4 中,所有的I/O操作和事件都由已经被分配给了EventLoop的那个Thread来处理。

    任务调度

    偶尔,你将需要调度一个任务以便稍后(延迟)执行或者周期性地执行。例如,你可能想要注册一个在客户端已经连接了5 分钟之后触发的任务。一个常见的用例是,发送心跳消息到远程节点,以检查连接是否仍然还活着。如果没有响应,你便知道可以关闭该Channel 了。

    在内部,当提交任务到如果(当前)调用线程正是支撑EventLoop 的线程,那么所提交的代码块将会被(直接)执行。否则,EventLoop 将调度该任务以便稍后执行,并将它放入到内部队列中。当EventLoop下次处理它的事件时,它会执行队列中的那些任务/事件。

    异步传输

    异步传输实现只使用了少量的EventLoop(以及和它们相关联的Thread),而且在当前的线程模型中,它们可能会被多个Channel 所共享。这使得可以通过尽可能少量的Thread 来支撑大量的Channel,而不是每个Channel 分配一个Thread。EventLoopGroup 负责为每个新创建的Channel 分配一个

    EventLoop。在当前实现中,使用顺序循环(round-robin)的方式进行分配以获取一个均衡的分布,并且相同的EventLoop可能会被分配给多个Channel。

    一旦一个Channel 被分配给一个EventLoop,它将在它的整个生命周期中都使用这个EventLoop(以及相关联的Thread)。请牢记这一点,因为它可以使你从担忧你的Channel-

    Handler 实现中的线程安全和同步问题中解脱出来。

    另外,需要注意的是,EventLoop 的分配方式对ThreadLocal 的使用的影响。因为一个EventLoop 通常会被用于支撑多个Channel,所以对于所有相关联的Channel 来说,ThreadLocal 都将是一样的。这使得它对于实现状态追踪等功能来说是个糟糕的选择。然而,

    在一些无状态的上下文中,它仍然可以被用于在多个Channel 之间共享一些重度的或者代价昂贵的对象,甚至是事件。

    ChannelFuture 接口

    Netty 中所有的I/O 操作都是异步的。因为一个操作可能不会立即返回,所以我们需要一种用于在之后的某个时间点确定其结果的方法。为此,Netty 提供了ChannelFuture 接口,其addListener()方法注册了一个ChannelFutureListener,以便在某个操作完成时(无论是否成功)得到通知。可以将

    ChannelFuture 看作是将来要执行的操作的结果的占位符。它究竟什么时候被执行则可能取决于若干的因素,因此不可能准确地预测,但是可以肯定的是它将会被执行。

    ChannelHandler、ChannelPipeline和ChannelHandlerContext 

    ChannelHandler

    从应用程序开发人员的角度来看,Netty 的主要组件是ChannelHandler,它充当了所有处理入站和出站数据的应用程序逻辑的容器。ChannelHandler 的方法是由网络事件触发的。事实上,ChannelHandler 可专门用于几乎任何类型的动作,例如将数据从一种格式转换为另外一种格式,例如各种编

    解码,或者处理转换过程中所抛出的异常。

    举例来说,ChannelInboundHandler 是一个你将会经常实现的子接口。这种类型的ChannelHandler 接收入站事件和数据,这些数据随后将会被你的应用程序的业务逻辑所处理。当你要给连接的客户端发送响应时,也可以从ChannelInboundHandler 冲刷数据。你的应用程序的业务逻辑通常驻留在

    一个或者多个ChannelInboundHandler 中。

    这种类型的ChannelHandler 接收入站事件和数据,这些数据随后将会被你的应用程序的业务逻辑所处理。

    ChannelHandler 的生命周期

    下面列出了interface ChannelHandler 定义的生命周期操作,在ChannelHandler被添加到ChannelPipeline 中或者被从ChannelPipeline 中移除时会调用这些操作。这些方法中的每一个都接受一个ChannelHandlerContext 参数。

    handlerAdded 当把ChannelHandler 添加到ChannelPipeline 中时被调用

    handlerRemoved 当从ChannelPipeline 中移除ChannelHandler 时被调用

    exceptionCaught 当处理过程中在ChannelPipeline 中有错误产生时被调用

    Netty 定义了下面两个重要的ChannelHandler 子接口:

      ChannelInboundHandler——处理入站数据以及各种状态变化;

      ChannelOutboundHandler——处理出站数据并且允许拦截所有的操作。

    ChannelInboundHandler 接口

    下面列出了interface ChannelInboundHandler 的生命周期方法。这些方法将会在数据被接收时或者与其对应的Channel 状态发生改变时被调用。正如我们前面所提到的,这些方法和Channel 的生命周期密切相关。

    channelRegistered 当Channel 已经注册到它的EventLoop 并且能够处理I/O 时被调用

    channelUnregistered 当Channel 从它的EventLoop 注销并且无法处理任何I/O 时被调用

    channelActive 当Channel 处于活动状态时被调用;Channel 已经连接/绑定并且已经就绪

    channelInactive 当Channel 离开活动状态并且不再连接它的远程节点时被调用

    channelReadComplete 当Channel上的一个读操作完成时被调用

    channelRead 当从Channel 读取数据时被调用

    ChannelWritability-Changed 当Channel 的可写状态发生改变时被调用。用户可以确保写操作不会完成得太快(以避免发生OutOfMemoryError)或者可以在Channel 变为再次可写时恢复写入。可以通过调用Channel 的isWritable()方法来检测Channel 的可写性。与可写性相关的阈值可以通过

    Channel.config().setWriteHighWaterMark()和Channel.config().setWriteLowWater-Mark()方法来设置

    userEventTriggered 当ChannelnboundHandler.fireUserEventTriggered()方法被调用时被调用。

    当某个ChannelInboundHandler 的实现重写channelRead()方法时,它要负责显式地释放与池化的ByteBuf 实例相关的内存。Netty 为此提供了一个实用方法ReferenceCount-Util.release()。Netty 将使用WARN 级别的日志消息记录未释放的资源,使得可以非常简单地在代码中发现违规的实例。但是以这种方式管理资源可能很繁琐。一个更加简单的方式是使用Simple-ChannelInboundHandler,SimpleChannelInboundHandler 会自动释放资源。

    ChannelOutboundHandler 接口

    出站操作和数据将由ChannelOutboundHandler 处理。它的方法将被Channel、Channel-Pipeline 以及ChannelHandlerContext 调用。

    所有由ChannelOutboundHandler 本身所定义的方法:

    bind(ChannelHandlerContext,SocketAddress,ChannelPromise) 当请求将Channel 绑定到本地地址时被调用

    connect(ChannelHandlerContext,SocketAddress,SocketAddress,ChannelPromise) 当请求将Channel 连接到远程节点时被调用

    disconnect(ChannelHandlerContext,ChannelPromise) 当请求将Channel 从远程节点断开时被调用

    close(ChannelHandlerContext,ChannelPromise) 当请求关闭Channel 时被调用

    deregister(ChannelHandlerContext,ChannelPromise) 当请求将Channel 从它的EventLoop 注销时被调用

    read(ChannelHandlerContext) 当请求从Channel 读取更多的数据时被调用

    flush(ChannelHandlerContext) 当请求通过Channel 将入队数据冲刷到远程节点时被调用

    write(ChannelHandlerContext,Object,ChannelPromise) 当请求通过Channel 将数据写到远程节点时被调用

    ChannelHandler的适配器

    有一些适配器类可以将编写自定义的ChannelHandler 所需要的努力降到最低限度,因为它们提供了定义在对应接口中的所有方法的默认实现。因为你有时会忽略那些不感兴趣的事件,所以Netty提供了抽象基类ChannelInboundHandlerAdapter 和ChannelOutboundHandlerAdapter。

    你可以使用ChannelInboundHandlerAdapter 和ChannelOutboundHandlerAdapter类作为自己的ChannelHandler 的起始点。这两个适配器分别提供了ChannelInboundHandler和ChannelOutboundHandler 的基本实现。通过扩展抽象类ChannelHandlerAdapter,它们获得了它们共同的超接口

    ChannelHandler 的方法。

    ChannelHandlerAdapter 还提供了实用方法isSharable()。如果其对应的实现被标注为Sharable,那么这个方法将返回true,表示它可以被添加到多个ChannelPipeline。

    ChannelPipeline

    将图 中的处理器(ChannelHandler)从左到右进行编号,那么第一个被入站事件看到的ChannelHandler 将是 1,而第一个被出站事件看到的ChannelHandler 将是5。

    当Channel 被创建时,它会被自动地分配到它专属的ChannelPipeline。每一个新创建的Channel 都将会被分配一个新的ChannelPipeline。这项关联是永久性的;Channel 既不能附加另外一个ChannelPipeline,也不能分离其当前的。在Netty 组件的生命周期中,这是一项固定的操作,不需要开发

    人员的任何干预。使得事件流经ChannelPipeline 是ChannelHandler 的工作,它们是在应用程序的初始化或者引导阶段被安装的。这些对象接收事件、执行它们所实现的处理逻辑,并将数据传递给链中的下一个ChannelHandler。它们的执行顺序是由它们被添加的顺序所决定的。入站和出站

    ChannelHandler 可以被安装到同一个ChannelPipeline中。如果一个消息或者任何其他的入站事件被读取,那么它会从ChannelPipeline 的头部开始流动,最终,数据将会到达ChannelPipeline 的尾端,届时,所有处理就都结束了。数据的出站运动(即正在被写的数据)在概念上也是一样的。在这

    种情况下,数据将从ChannelOutboundHandler 链的尾端开始流动,直到它到达链的头部为止。在这之后,出站数据将会到达网络传输层,这里显示为Socket。通常情况下,这将触发一个写操作。如果将两个类别的ChannelHandler都混合添加到同一个ChannelPipeline 中会发生什么。虽然

    ChannelInboundHandle 和ChannelOutboundHandle 都扩展自ChannelHandler,但是Netty 能区分ChannelInboundHandler实现和ChannelOutboundHandler 实现,并确保数据只会在具有相同定向类型的

    两个ChannelHandler 之间传递。

    ChannelPipeline上的方法

    addFirst、addBefore、addAfter、addLast 将一个ChannelHandler 添加到ChannelPipeline 中

    remove 将一个ChannelHandler 从ChannelPipeline 中移除

    replace 将ChannelPipeline 中的一个ChannelHandler 替换为另一个ChannelHandler

    get 通过类型或者名称返回ChannelHandler

    context 返回和ChannelHandler 绑定的ChannelHandlerContext

    names 返回ChannelPipeline 中所有ChannelHandler 的名称

    ChannelPipeline 的API 公开了用于调用入站和出站操作的附加方法。

    ChannelHandlerContext 

    通过使用作为参数传递到每个方法的ChannelHandlerContext,事件可以被传递给当前ChannelHandler 链中的下一个ChannelHandler。虽然这个对象可以被用于获取底层的Channel,但是它主要还是被用于写出站数据。

    ChannelHandlerContext 代表了ChannelHandler 和ChannelPipeline 之间的关联,每当有ChannelHandler 添加到ChannelPipeline 中时,都会创建ChannelHandler-Context。ChannelHandlerContext 的主要功能是管理它所关联的ChannelHandler 和在同一个ChannelPipeline 中的其他

    ChannelHandler 之间的交互。ChannelHandlerContext 有很多的方法,其中一些方法也存在于Channel 和Channel-Pipeline 本身上,但是有一点重要的不同。如果调用Channel 或者ChannelPipeline 上的这些方法,它们将沿着整个ChannelPipeline 进行传播。而调用位于ChannelHandlerContext上的相同方法,则将从当前所关联的ChannelHandler 开始,并且只会传播给位于该ChannelPipeline 中的下一个(入站下一个,出站上一个)能够处理该事件的ChannelHandler。

    ChannelHandlerContext 的API

    alloc 返回和这个实例相关联的Channel 所配置的ByteBufAllocator

    bind 绑定到给定的SocketAddress,并返回ChannelFuture

    channel 返回绑定到这个实例的Channel

    close 关闭Channel,并返回ChannelFuture

    connect 连接给定的SocketAddress,并返回ChannelFuture

    deregister 从之前分配的EventExecutor 注销,并返回ChannelFuture

    disconnect 从远程节点断开,并返回ChannelFuture

    executor 返回调度事件的EventExecutor

    fireChannelActive 触发对下一个ChannelInboundHandler 上的channelActive()方法(已连接)的调用

    fireChannelInactive 触发对下一个ChannelInboundHandler 上的channelInactive()方法(已关闭)的调用

    fireChannelRead 触发对下一个ChannelInboundHandler 上的channelRead()方法(已接收的消息)的调用

    fireChannelReadComplete 触发对下一个ChannelInboundHandler 上的channelReadComplete()方法的调用

    fireChannelRegistered 触发对下一个ChannelInboundHandler 上的fireChannelRegistered()方法的调用

    fireChannelUnregistered 触发对下一个ChannelInboundHandler 上的fireChannelUnregistered()方法的调用

    fireChannelWritabilityChanged 触发对下一个ChannelInboundHandler 上的fireChannelWritabilityChanged()方法的调用

    fireExceptionCaught 触发对下一个ChannelInboundHandler 上的fireExceptionCaught(Throwable)方法的调用

    fireUserEventTriggered 触发对下一个ChannelInboundHandler 上的fireUserEventTriggered(Object evt)方法的调用

    handler 返回绑定到这个实例的ChannelHandler

    isRemoved 如果所关联的ChannelHandler 已经被从ChannelPipeline中移除则返回true

    name 返回这个实例的唯一名称

    pipeline 返回这个实例所关联的ChannelPipeline

    read 将数据从Channel读取到第一个入站缓冲区;如果读取成功则触发一个channelRead事件,并(在最后一个消息被读取完成后)通知ChannelInboundHandler 的channelReadComplete(ChannelHandlerContext)方法

    当使用ChannelHandlerContext 的API 的时候,有以下两点:

    • ChannelHandlerContext 和ChannelHandler 之间的关联(绑定)是永远不会改变的,所以缓存对它的引用是安全的;
    • 如同我们在本节开头所解释的一样,相对于其他类的同名方法,ChannelHandler Context的方法将产生更短的事件流,应该尽可能地利用这个特性来获得最大的性能。

    选择合适的内置通信传输模式

    NIO io.netty.channel.socket.nio 使用java.nio.channels 包作为基础——基于选择器的方式

    Epoll io.netty.channel.epoll 由 JNI 驱动的 epoll()和非阻塞 IO。这个传输支持只有在Linux 上可用的多种特性,如SO_REUSEPORT,比NIO 传输更快,而且是完全非阻塞的。将NioEventLoopGroup替换为EpollEventLoopGroup , 并且将NioServerSocketChannel.class 替换为

    EpollServerSocketChannel.class 即可。

    OIO io.netty.channel.socket.oio 使用java.net 包作为基础——使用阻塞流

    Local io.netty.channel.local 可以在VM 内部通过管道进行通信的本地传输

    Embedded io.netty.channel.embedded Embedded 传输,允许使用ChannelHandler 而又不需要一个真正的基于网络的传输。在测试ChannelHandler 实现时非常有用

  • 相关阅读:
    day50——前端简介、标签分类、常用标签
    day46——约束条件、表与表建 关系、修改表的完整语法
    day45——存储引擎、数据类型、约束条件
    day44——存储数据的发展、数据库分类、mysql
    Ⅰ:计算机核心基础
    Ⅶ:基本数据类型及内置方法
    Ⅶ:作业
    Ⅵ:深浅copy
    Ⅵ:流程控制
    Ⅳ:运算符
  • 原文地址:https://www.cnblogs.com/alimayun/p/12500842.html
Copyright © 2011-2022 走看看