Read book Netty in action(Chapter Chapter IV)--Netty‘s components and design

序言

之前曾经写过一个demo for netty,但是很显然,并没有将netty融汇贯通,所以需要学习其设计和组件来更进一步,最后再去阅读其源码以至完全吸收netty并化为自己实力的一部分。(学习任何框架最后的终点都是阅读其源码,这样想用就用,出问题知道如何修复)。

Channel、EventLoop和ChannelFuture

我们不管是写服务端还是客户端,都会写这样的代码:

 public  void start() {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoClientChannelHandler());
                        }
                    });
            ChannelFuture future = bootstrap.connect().sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                group.shutdownGracefully().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

这正是Channel、EventLoop和ChannelFuture的运用,现在正要对其逐个剖析。

channel - sockent(通道)
eventLoop-控制流、多线程处理器、并发
channelFuture - 异步通知,其实就是和惯用的 FutureTask一个原理。

Channel接口

基本的I/O操作(bind()、connect()、read()、write())都依赖底层网络传输接口。在基于java的网络编程中,其基本的构造是Socket。Netty的Channel接口所提供的api正是降低了Socket使用的复杂性。此外。channel中也提供了很多实现。可以去源码中查看。

eventLoop

eventLoop定义了netty的核心抽象,用于处理连接生命周期所发生的事。后面将对其进行详细的讨论。首先一个EventLoopGroup中有多个EventLoop。创建了一个channel的时候会将其注册到evnetloop中,以便以在整个生命周期中处理I/O事件,对目前的我的认知而言,一切并发问题起源都是IO造成的。当然合理的复杂度会使我的程序更加高效。例如:我并不惯用递归。因为深不见底,更可怕的是你无法掌控它的深度以至于会不会将栈溢出都是未知数。
一个EventLoopGroup中有多个EventLoop。
一个EventLoop在他的生命周期中只和一个Thread绑定。
所有由EventLoop处理的I/O事件都将在他专用的Thread上处理。
一个EventLoop可能会分配给一个或者多个EventLoop。
实际上一个 通道中所有的I/O操作都是一个Thread处理的。所以不存在同步不同步而定。

ChannelFuture接口

Netty中的所有I/O操作都不会立刻得到结果,但是你都会得到结果,你只要

addListener(ChannelFutureListener.CLOSE);

注册一个ChannelFutureListener便会在某个操作完成的时候得到通知。(无论结果是成功失败)

ChannelHandler和ChannelPipeline

ChannelHandler接口

Netty的主要组件对于一个 java developer来说其实是ChannelHandler,它充当了所有处理入站和出站数据应用程序逻辑的容器。因为它的方法是由网络事件触发的。ChannelHandler可以专门用于任何类型的动作。例如转化数据,或者处理转化数据所抛出的异常。
ChannelInboundHandler是一个我们经常用的例子。是一个子接口,用来接收入站事件和出站事件,这些数据随后会被业务接纳并且处理。当给连接的客户端发送响应的时候,也是从ChannelInboundHandler冲刷数据。例如:

@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf in = (ByteBuf) msg;
        System.out.println("Server received :" + ((ByteBuf) msg).toString(CharsetUtil.UTF_8));
        ctx.write(in);
    }

所以我处理应用数据的业务逻辑应该在一个或者等多个ChannelInboundHandler中。

ChannelPipeline接口

ChannelPipeline为ChannelHandler提供了链路,我们曾经用过它。例如:

childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoChannelHandler());
                        }
                    });

追源码一下:

   ChannelPipeline pipeline();
   // 获取一个pipeline
 private final DefaultChannelPipeline pipeline;

addLast

 private void addLast0(AbstractChannelHandlerContext{
      // ... 
    private final Channel channel;
}
          // ... 

从这里就能看出来。多的代码不放了。

ChannelHandler又是如何和ChannelPipeline扯上关系的呢?其实可以再浅看一下源码(当有问题,看源码吧。不会使用,看源码吧。怎么快速上手学习,还是看源码吧。源码万能!!!

首先,当你实现了ChannelInitializer的时候,会看到这个

  public B handler(ChannelHandler handler) {
        this.handler = ObjectUtil.checkNotNull(handler, "handler");
        return self();
    }

这意味着ChannelInitializer的实现被注册到了BootStrap中。不换是服务端还是客户端的。
当initChannel方法被调用的时候,

  @Override
    @SuppressWarnings("unchecked")
    public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        // Normally this method will never be called as handlerAdded(...) should call initChannel(...) and remove
        // the handler.
        if (initChannel(ctx)) {
            // we called initChannel(...) so we need to call now pipeline.fireChannelRegistered() to ensure we not
            // miss an event.
            ctx.pipeline().fireChannelRegistered();

            // We are done with init the Channel, removing all the state for the Channel now.
            removeState(ctx);
        } else {
            // Called initChannel(...) before which is the expected behavior, so just forward the event.
            ctx.fireChannelRegistered();
        }
    }

继续追:

    @Override
    public final ChannelPipeline fireChannelRegistered() {
        AbstractChannelHandlerContext.invokeChannelRegistered(head);
        return this;
    }
void channelRegistered(ChannelHandlerContext ctx) throws Exception;

我至少应该知道ChannelPipeline 为自己安装/注册了ChannelHandler.
最后移除了ChannelInitializer。

首先,ChannelHandler是处理事件的处理器。一个line(一下简称ChannelPipeline 为line),中应该有很多事件,这些事件有些是初始化安装的,有些是引导安装的。他们是有序的双向链表(可以看做),他们执行的顺序是安装顺序决定的。所以简而言之,对我而言Channel只是一个通道,当这个通道被创建的时候,就会有与之对应的line,这个line中是一个处理器的链,挨个处理事件,给服务器和客户端的通道,可能服务器给客户端事件,也有可能客户端给服务端的,组成了这个服务端和客户端的交互。当一个 这是我的理解。

之前我们谈入站、出站。首先一个事件的运动方向如果是从服务端到客户端,我们就称为入站事件,不然就是出站事件,可以脑补一下 jvm中的栈,栈底为脑补客户端,你是服务器,往里面invoke,进栈了,完事了回到你手上,就出栈。

编码器和解码器

当你通过netty去发送或者接受一个消息的时候,就会发送一次数据转换。入站消息就会被解码,从JAVA字节码转为另一个格式,通常是一个Java对象。如果是出站,就是相反方向的。因为网络数据总是一连串的字节。
对于特定的需要,Netty为编码器和解码器提供了不同类型的抽象类。程序可能以一种中间格式的方式传递,那么没必要将消息立马转为字节码。正如ChannelHandler。所有由Netty提供的编码器/解码器适配类,都实现了ChannelInboundHandler或者ChannelOutboundHandler,其实入站数据来说,channelRead方法以及被重写了。随后将会解码,由链结构传递给下一个处理器。出站则是反的。将消息转为字节码,然后传给下一个。这里只是简单介绍,具体使用以后会提。

引导

Netty的引导类为应用程序的网络层配置提供了容器,这将涉及将一个进程绑定到某个指定的端口,或者将一个进程连接到另一个正在运行的主机端口进行绑定。
我们之前曾经建立过应用,称之为:服务器、客户端。实际上是表示了不同的网络行为;换句话说,是监听传入的连接还是建立一个或者多个进程的连接。
因此,有两种类型的引导:一种作用于客户端,一类作用于服务器。即 BootStrap表示连接到远程主机和端口和ServerBootStrap表示绑定一个本地端口。客户端只需要一个EventLoopGroup,但是服务器则需要两个。因为服务器需要两组不同的Channel。第一组只包含ServerChannel表示服务器已经绑定到本地端口正在监控套接字。第二组则用来处理连接。和ServerChannel相关的EventLoopGroup将分配一个EventLoop。它将负责为传入的连接请求创建Channel;。一旦被接受,那么第二个EventLoopGroup将会给Channel分配一个EventLoop。

结束语

这次主要看的是Netty的一些组件和设计,我们到现在应该可以简单的运用netty并且知道如何去用了,后面将会更加深入的了解Netty。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值