Netty的初步认识与理解

222 阅读5分钟

1 Netty的组件作用

1.1 ServerBootstrap和Bootstrap的作用以及相关参数用途

这两个组件是用来对服务端和客户端进行参数的设置,group的作用是为服务器设置接受请求的线程池和处理请求的线程池,boosgroup中的线程一般只用来接受一手的请求也被称为mainreactor,而后续对请求进行监听以及为读写请求分配线程的subreactor在workergroup中,处理读写请求的线程也在workergroup中分配。channel用来设置管道类型。atter用来为cannel设置属性,可以用来为服务端和客户端进行交互以及记录一些基本信息。optionchildoption都是用来设置连接属性的。childHandler用来设置当服务端接受到一个请求之后需要经过的处理器。ChannelInitializer会为每一个请求进行一次初始化操作。

serverBootstrap
  .group(boosGroup, workerGroup)
  .channel(NioServerSocketChannel.class)
  .attr(AttributeKey.newInstance("name"), "value")//给服务端cannel维护一个map
  .childAttr(AttributeKey.newInstance("name"), "value")//给客户端cannel维护一个map
  .option(ChannelOption.SO_BACKLOG, 1024)//设置临时存放三次握手请求队列的最大长度
  .childOption(ChannelOption.SO_KEEPALIVE, true)//配置tcp属性,开启心跳机制
  .childOption(ChannelOption.TCP_NODELAY, true)//配置tcp属性,开启节点延迟
  .childHandler(new ChannelInitializer<NioSocketChannel>() {
    protected void initChannel(NioSocketChannel ch) {
      ch.pipeline().addLast(new processor());
    }
  });

1.2 Handler和Pipeline的使用对应的优化

Handler一般继承Channel(In/outDuplex)boundHandlerAdapter来覆盖一些首次连接执行的接口、读接口、写接口、断开链接执行的接口来实现对不同处理器的定制。Pipeline是每一个通道特有的一个处理链,类似于设计模式中的策略模式中的策略链,当一个请求进来的时候会按照处理器加入到管道的顺序依次经过处理,而写请求就是管道中的顺序倒过来的,这样一个请求进来时候经过这样一系列的处理就完成了这样一次处理,比如下方代码处理链就是A->B->BB->AA。提高效率通常来说一个请求是不需要经过所有处理器,所以可以在处理器中判断接受的包的类型来选择是否使用当前处理器,但是在netty中已经考虑到了这种情况所以可以直接通过继承SimpleChannelInboundHandler类,netty内部会进行对象的类型是否匹配来判断是否执行该处理器。

childHandler(new ChannelInitializer<NioSocketChannel>() {
  protected void initChannel(NioSocketChannel channel) {
    channel.pipeline().addLast(new inA());
    channel.pipeline().addLast(new inB());
    channel.pipeline().addLast(new outAA());
    channel.pipeline().addLast(new outBB());
  }
});

1.3 ByteBuf之消息的传递以及粘包的解决

消息在传递中依靠的是字节传递,而这个信息的代表就是ByteBuf(这个对象在创建的时候分成堆内创建和堆外创建),我们通过操作ByteBuf来读写字节信息,而我们的对象可以通过一个规定好的规则进行读写,这就是协议,可以通过协议来对消息进行更深层次的记录包括要调用的方法,以及被传递对象的类信息。这些消息最后通过写入管道进行发送。

粘包问题的来源是消息是靠字节传递的所以在被重新拼装成bytebuf的时候有可能是不对称的,而我们自己处理的话可以不断判断是否是一个完整的包来进行处理,但是netty中有四个自带的拆包器,固定长度拆包器FixedLengthFrameDecoder、行拆包器LineBasedFrameDecoder、分隔符 DelimiterBasedFrameDecoder,基于长度变量拆包器LengthFieldBasedFrameDecoder,我们一般使用最后一种,这样可以传递的消息长度不规则。

//写
ByteBuf buffer = encodeprocessor(context.alloc(), loginRequestPacket);
context.channel().writeAndFlush(buffer);
//读
ByteBuf buffer = (ByteBuf) msg;
Myobject myobject = decodeprocessor(buffer);
//拆包器
LengthFieldBasedFrameDecoder(数据包最大长度, 长度域偏移量, 长度域长度)

2 Netty传递信息的流程

客户端请求连接服务器->服务器创建一个管道并且以一条处理器链来初始化->调用处理器中当连接加入时就调用的方法->等待读写请求->客户端输入请求->按照约定好的序列化方式进行编码处理并且发送->服务端接受信息进行拆成一个完整包的处理->利用约定好的反序列化方式进行解码->调用服务端处理器链符合该包类型的处理器->处理完成后将应答包编码发送回去->后面同上

3 对于Netty的思考

3.1 不同语言之间如何通信

因为在传输过程中不同端之间依靠着字节的方式交流,所以只需要采用跨语言的序列化工具就能达成不同语言之间的通信的效果,类似于采用protobuf或者thrift作为序列化工具。

3.2 Netty在我们的项目中扮演什么角色

根据最近的使用以及体会:Netty更多情况下只是一个通信的作用,管理着接受请求和处理请求的线程,而我们所要做的就是定制我们需要的处理器来处理netty管道中传输进来的字节信息,而在这些处理器中为了方便交互需要序列化工具所以就有了序列化处理器,最后就是我们的逻辑部分,目前来看Netty中我们所使用到的就是这几个部分,而后面为了提高健壮性或者存储中间数据等操作都是在逻辑部分自行处理的,所以实际上Netty只是一个基于字节流的通信工具,而Netty中自带的其他组件只是一些附加组件类似于http编解码器等。