6

Netty4 Channel 概述(通道篇)

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzIzNzgyMjYxOQ%3D%3D&%3Bmid=2247485452&%3Bidx=1&%3Bsn=b1d696fc06efd5bbf4ad04fd25531b5d
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

点击上方 “中间件兴趣圈” 选择 “设为星标”

做积极的人,越努力越幸运! 6rAF3eF.png!mobile

1、通道概述

我们从如下几个方面来简单了解一下 Channel。

  • 通道的当前状态,open(端口打开)、connect(连接)。

  • 通道的配置,包含通道的配置属性与网络通信选项(ChannelOption)。

  • IO 通道方法诸如 read、write、connect、bind 与管道(ChannelPipeline)。

  • 所有 IO 操作在 Netty 中都是异步的,调用 IO 方法例如 write 方法后,并不是等 IO 操作实际完成后再返回,而是会立即返回一个凭证,IO 操作完成后会将结果写入凭证中,典型的 Future设计模式。

  • Channel 具有父子关系,由于所有的 SocketChannel(客户端发起TCP连接)都是由 ServerSocketChannel(服务端接收连接)接收客户端连接而创建的,故 SocketChannel 的 parent() 方法会返回对应的 ServerSocketChannel。

  • 所有通道对象在使用完后,请务必调用通道的colse方法来释放资源。

本节将从如下3个方面来重点介绍Channel。

  • Channel 常用API

  • Channel 配置与选项

  • NIO相关的Channel继承图

2、Channel常用API

Channel 类图结构如下:

Uv6rEfq.jpg!mobile

核心API一览:

  • EventLoop eventLoop()

    返回该通道注册的事件轮询器。

  • Channel parent()

    返回该通道的父通道,如果是ServerSocketChannel实例则返回null,SocketChannel实例则返回对应的ServerSocketChannel。

  • ChannelConfig config()

    返回该通道的配置参数。

  • boolean isOpen()

    端口是否处于open,通道默认一创建isOpen方法就会返回true,close方法被调用后该方法返回false。

  • boolean isRegistered()

    是否已注册到EventLoop。

  • public boolean isActive()

    通道是否处于激活。NioSocketChannel的实现是java.nio.channels.SocketChannel实例的isOpen()与isConnected()都返回true。NioServerSocketChannel的实现是ServerSocketChannel.socket().isBound(),如果绑定到端口中,意味着处于激活状态。

  • ChannelFuture closeFuture()

    Future 模式的应用,调用该方法的目的并不是关闭通道,而是预先创建一个凭证(Future),等通道关闭时,会通知该 Future,用户可以通过该 Future 注册事件。

  • ChannelFuture bind(SocketAddress localAddress)

    Netty 服务端绑定到本地端口,开始监听客户端的连接请求。该过程会触发事件链(ChannelPipeline)。该部分将在后续讲解服务端启动流程时再详细分析。

  • ChannelFuture connect(SocketAddress remoteAddress)

    Netty客户端连接到服务端,该过程同样会触发一系列事件(ChannelPipeline)。该部分将在后续讲解客户端启动流程时再详细分析。

  • ChannelFuture disconnect()

    断开连接,但不会释放资源,该通道还可以再通过connect重新与服务器建立连接。

  • ChannelFuture close()

    关闭通道,回收资源,该通道的生命周期完全结束。

  • ChannelFuture deregister()

    取消注册。

  • Channel read()

    通道读,该方法并不是直接从读写缓存区读取文件,而是向NIO Selecor注册读事件(目前主要基于NIO)。当通道收到对端的数后,事件选择器会处理读事件,从而触发ChannelInboundHandler#channelRead 事件,然后继续触发ChannelInboundHandler#channelReadComplete(ChannelHandlerContext)事件。

  • ChannelFuture write(Object msg)

    向通道写字节流,会触发响应的写事件链,该方法只是会将字节流写入到通道缓存区,并不会调用flush方法写入通道中。

  • Channel flush()

    刷写所有挂起的消息(刷写到流中)。

  • ChannelFuture writeAndFlush(Object msg)

    相当于调用write与flush方法。

3、Channel配置与选项

3.1 Channel配置

ChannelConfig 的类图如下:

je6N32a.jpg!mobile

核心配置如下:

  • Map<ChannelOption<?>, Object> options:网络相关的配置属性。

    <channeloption</channeloption

  • int connectTimeoutMillis:连接超时时间。

  • int maxMessagesPerRead:每次读事件中调用读方法的最大次数(AbstractNioByteChannel)或读事件循环中最多处理的消息条数(AbstractNioMessageChannel)。

  • int writeSpinCount:一次写事件处理期间最多调用write方法的次数,引入该机制主要是为了避免一个网络通道写入大量数据,对其他网络通道的读写处理带来延迟,默认值为16。

  • ByteBufAllocator getAllocator():返回该通道的内存分配器(ByteBuf)。

    RecvByteBufAllocator getRecvByteBufAllocator():读事件读缓冲区的分配策略。

  • boolean autoRead:是否自动触发read方法调用,默认为true,读事件触发后自动调用read方法 ,而无需应用程序显示调用。

  • int writeBufferHighWaterMark:设置写缓存区的高水位线。如果写缓存区中的数据超过该值,Channel#isWritable()方法将返回false。

  • int writeBufferLowWaterMark:设置写缓存区的低水位线。如果写缓存区的数据超过高水位线后,通道将变得不可写,等写缓存数据降低到低水位线后通道恢复可写状态(Channel#isWritable()将再次返回true)。

3.2 ChannelOption

f26bQrq.jpg!mobile网络通道(Channel)选项值,下面介绍一下与TCP协议相关的核心参数:
  • SO_BROADCAST

    选择值类型:boolean。表示该数据包是否是广播包,true表示广播包,false表示非广播,如果包的IP地址为广播地址,但该选型为false,则在内核层会抛出错误。

  • SO_KEEPALIVE 对于面向连接的TCP socket,在实际应用中通常都要检测对端是否处于连接中,连接端口分两种情况:

    • 连接正常关闭,调用close() shutdown()连接优雅关闭,send与recv立马返回错误,select返回SOCK_ERR

    • 连接的对端异常关闭,比如网络断掉,突然断电.

  • SO_SNDBUF的大小

    为了达到最大网络吞吐,socket send buffer size(SO_SNDBUF)不应该小于带宽和延迟的乘积。

  • SO_REUSEADDR

    该参数如果设置为true的一个常用应用场景是端口复用(直接复用TIME_WAIT状态的socket)。

  • SO_LINGER

    该参数是控制TCP关闭行为的。

  • SO_BACKLOG

    服务端接受客户端连接的处理队列,在TCP三次握手协议中,服务端接收到客户端的SYN包后,会向客户端发送SYN+ACK包,同时会将连接放入到 backlog 队列中,等待客户端ACK包。在服务端没有接收到客户端的ACK包之前,连接会暂存 backlog 队列。

  • SO_TIMEOUT

    以毫秒为单位定义套接字超时(SO_TIMEOUT),它是等待数据的超时,或者换句话说,是两个连续数据包之间的最大活动周期。超时值为0将被解释为无限超时。如果没有设置该参数,读取操作将不会超时(无穷小超时)。个人思考:在NIO编程开发中应该不要设置该值,但为了保证每个连接的读平等,Netty会控制一次事件选择周期,最多可调用read方法的次数。

  • TCP_NODELAY

    在 TCP 数据包发送的时候,有一种算法(Nagle算法)。该算法的核心是如果发生数据包比较小,为了提高带宽的利用率,会等待更多的数据到达后再发送或等待超时后将小包发送,也就是 TCP 发送延迟,TCP_NODELAY = true表示不使用 tcp delay 延迟,故禁用 Nagle 算法。通常接受端的 ACK 包也会使用延迟(默认40ms),旨在合并多个 ACK 确认包。

4、Channel NIO 继承图

Channel 类继承图主要是想展示一下与 NIO 相关的 NioSocketChannel (客户端通道)与NioServerSocketChannel (服务端通道)在 Channel 中的位置。

VFV3yqE.png!mobile

Channel 通道统一抽象接口。

  • AbstractChannel 通道默认抽象实现类

  • AbstractEpollChannel unix Epoll通道实现

  • AbstractOioChannel 阻塞IO通道抽象类

  • AbstractNioChannel NIO通道抽象类

  • AbstractNioByteChannel NIO客户端通道抽象类

  • AbstractNIoMessageChannel NIO服务端通道抽象类

  • NioSocketChannel  NIO客户端通道实现类

  • NioServerSocketChannel NIO服务端通道实现类

本文就介绍到这里了,本篇作为 Netty4专栏的第一篇文章,主要是介绍 Netty Channel 的基本知识,后续会重点以 NIO 为例,详细剖析通道的相关实现原理。

学习 Netty 需要具备 NIO 相关的基础,推荐哈罗出行基础架构部高级技术专家梁勇大佬的专栏:JAVA NIO 专栏

欢迎加入我的知识星球,一起 交流源码,探讨架构,揭秘亿级订单的架构设计与实践经验 ,打造高质量的技术交流圈,为广大星友提供高质量问答服务, 长按如下二维码加入。

fymYVbB.png!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK