/netty-note

netty in action 学习笔记

Primary LanguageJava

netty-note

1. Netty Intro

1. Netty构建块

1. Channel

和Java NIO的一个基本构造, 即可以看作是 输入,输出 的载体. 它可以被打开或关闭.

2. CallBack

就是平时认识的回调。 (JDK8 通过实现函数式接口来做回调)

3. Future

JDK的concurrent包里有个Future对象,但是:

  1. 是阻塞的.
  2. 需要手动检测任务是否完成. 因此, Netty重写了Future,即:ChannelFuture. 每次Channel的出站口都是返回一个ChannelFuture. 异步使用方法:
  • 向ChannelFuture中添加一个 ChannelFutureListener 对象:
    Channel channel = ...;
    ChannelFuture future = channel.connect( new InetSocketAdress("127.0.0.1", 8080) );
    future.addListener( new ChannelFutureListener(){
        // 继承了ChannelFutureListener的匿名类
        @Override
        public void operationComplete( ChannelFuture future ){
            if ( future.isSuccess() ){
                ...
            }else{
                ...
                // 如果发生错误, 则打印错误信息
                Throwable cause = new future.cause();
                cause.printStackTrace();
            }
        }
    } );

4. Event 和 ChannelHandler

netty使用不同的事件来通知我们状态的改变(即Event事件的发生,Reactor模型). 而每个ChannelHandler的实例都类似于一种为了响应特定事件而被执行的回调!

2. 第一个Netty程序

内容部分在README和代码注释里. experiment1

3. Netty的组件和设计

1. Channel接口

Channel是对socket的抽象 , socket的基本操作有: bind(), connect(), read(), write().

1. 预定义的常用Channel:

  1. EmbeddedChannel;
  2. LocalServerChannel;
  3. NioDatagramChannel;
  4. NioSctpChannel;
  5. NioSocketChannel;

2. Channel的方法:

  1. eventLoop(): 返回Channel处于的EventLoop
  2. pipline(): 返回Channel处于的ChannelPipline
  3. isActive(): 返回Channel是否处于活动状态; 活动的意义依赖于底层的传输协议. 例如, 一个socket传输一旦连接到了远程结点,那么就是活动的; 但一个Datagram传输一旦打开就是活动的;
  4. locaolAddress(): 返回本地的SocketAddress
  5. remoteAddress(): 返回远程的SocketAddress
  6. write(): 将数据写到远程结点. 这个数据被打包成msg传递给ChannelPipline,并且排队直到被冲刷(flush)
  7. flush(): 将之前已写的数据冲刷到底层传输.
  8. writeAndFlush(): 6+7=8

3. Channel是线程安全的

// 多个线程使用同一个Channel
final Channel channel = ...;
final ByteBuf buf = unPooled.copiedBuffer("your data", CharsetUtil.UTF_8).retain();

Runnable writer = new Runnable(){
    @Override
    public void run(){
        channel.writeAndFlush(buf.duplicate());
    }
}

// 开个线程池
Executor executor = Executors.newCachedThreadPool();

// 多线程发送数据
executor.execute(writer);
executor.execute(writer);
executor.execute(writer);

2. EventLoop接口

  1. 一个EventLoopGroup包含一个或多个EventLoop;
  2. 一个EventLoop在它的生命周期内只和一个Thread绑定;
  3. 所有由EventLoop处理的I/O事件都将在它专有的Thread上被处理;
  4. 一个Channel在它的生命周期内只注册于一个EventLoop;
  5. 一个EventLoop可能会被分配给一个或多个Channel

3. ChannelFuture接口

上面解释过了, 通过这个接口实现异步, 更详细的后面会讲.

4. ChannelHandler和ChannelPipline

1. ChannelHandler接口

网络事件触发时调用该接口的方法, 主要通过重写这类子接口(例如ChannelInboundHandler)的方法,来写我们的业务逻辑.

  • ChannelHandler的典型用途包括:
    1. 将数据从一种格式转换为另一种格式(decoder, encoder)
    2. 提供异常的通知
    3. 提供Channel变为活动的或者非活动的通知
    4. 提供当CHannel注册到EventLoop或者从EventLoop注销时的通知;
    5. 提供有关用户自定义事件的通知

2. ChannelPipeline接口

此接口是ChannelHandler链的容器 , Channel被创建时,会被放到专属的ChannelPipeline中. 下面说一下 ChannelHandler被注册到ChannelPipeline的过程:

  1. 一个ChannelInitializer的实例被注册到ServerBootstrap中;
  2. 通过调用ChannelInitializerinitChannel()方法来在ChannelPipline中安装一组自定义的ChannelHandler;
  3. ChannelInitializer讲自己从ChannelPipline中移除;

channelpipline

netty提供了抽象基类 ChannelInboundHandlerAdapterChannelOutboundHandlerAdapter 来提供过滤掉不感兴趣事件的手段. 其中, ChannelHandlerContext上的对应方法都提供了将事件传递给下一个ChannelHandler的方法.




在Netty中,有两种发送消息的方式:

  1. 直接写到Channel中.
    • 这种方式会导致发送的消息直接从Pipline的尾部开始流动。(依次经过ChannelOutboundHandler)
  2. 写到和ChannelHandler关联的ChannelHandlerContext中.
    • 这种方式会导致发送的消息从下一个ChannelHandler开始流动..

ps: 当ChannelHandler被注册到Pipline的时候,会为其分配一个CHannelHandlerContext, 代表了ChannelHandlerChannelPipline之间的绑定. 这个对象可以获取底层的Channel,其主要被用于写出站数据.

4. 传输

1. Netty提供的传输

名称 描述 应用
NIO io.netty.channel.socket.nio 使用java.nio.channels包作为基础——基于Selector方式 非阻塞代码库或一个常规的起点
Epoll io.netty.channel.epoll 由JNI驱动的epoll()和非阻塞IO。 这个传输支持 只有在Linux上可用的多种特性, 例如 SO_REUSEPORT,比NIO传输更快,并且是非阻塞的 非阻塞代码库或一个常规的起点(且在linux系统上)
OIO io.netty.channel.socket.io 使用java.net包作为基础——使用阻塞流 阻塞代码库
Local io.netty.channel.local 可以在VM内部通过管道进行通信的本地传输 在同一个JVM内进行通信
Embedded io.netty.channel.embedded Embedded传输, 允许使用ChannelHandler而又不需要一个真正的基于网络的传输. 在测试ChannelHandler的实现时非常有用 进行ChannelHandler的测试