channel是什么意思

更新时间:2022-12-28 13:38:39 阅读: 评论:0


2022年12月28日发(作者:正确对待金钱)

【Netty】Netty框架介绍

⼀、Netty简介

Netty是由JBOSS提供的⼀个java开源框架,现为Github上的独⽴项⽬。Netty提供异步的、事件驱动的⽹络应⽤程序框架和⼯具,⽤以

快速开发⾼性能、⾼可靠性的⽹络服务器和客户端程序。

也就是说,Netty是⼀个基于NIO的客户、服务器端的编程框架,使⽤Netty可以确保你快速和简单的开发出⼀个⽹络应⽤,例如实现了

某种协议的客户、服务端应⽤。Netty相当于简化和流线化了⽹络应⽤的编程开发过程,例如:基于TCP和UDP的socket服务开发。

“快速”和“简单”并不⽤产⽣维护性或性能上的问题。Netty是⼀个吸收了多种协议(包括FTP、SMTP、HTTP等各种⼆进制⽂本协议)的

实现经验,并经过相当精⼼设计的项⽬。最终,Netty成功的找到了⼀种⽅式,在保证易于开发的同时还保证了其应⽤的性能,稳定性和伸

缩性

1.1Netty的特点

设计优雅

适⽤于各种传输类型的统⼀API-阻塞和⾮阻塞Socket

基于灵活且可扩展的事件模型,可以清晰地分离关注点

⾼度可定制的线程模型-单线程,⼀个或多个线程池

真正的⽆连接数据报套接字⽀持(⾃3.1起)

使⽤⽅便

详细记录的Javadoc,⽤户指南和⽰例

没有其他依赖项,JDK5(Netty3.x)或6(Netty4.x)就⾜够了

⾼性能

吞吐量更⾼,延迟更低

减少资源消耗

最⼩化不必要的内存复制

安全

完整的SSL/TLS和StartTLS⽀持

社区活跃,不断更新

社区活跃,版本迭代周期短,发现的BUG可以被及时修复,同时,更多的新功能会被加⼊

1.2Netty常见使⽤场景

互联⽹⾏业

在分布式系统中,各个节点之间需要远程服务调⽤,⾼性能的RPC框架必不可少,Netty作为异步⾼新能的通信框架,往往作为基础通信

组件被这些RPC框架使⽤。

典型的应⽤有:阿⾥分布式服务框架Dubbo的RPC框架使⽤Dubbo协议进⾏节点间通信,Dubbo协议默认使⽤Netty作为基础通信组

件,⽤于实现各进程节点之间的内部通信。

游戏⾏业

⽆论是⼿游服务端还是⼤型的⽹络游戏,Java语⾔得到了越来越⼴泛的应⽤。Netty作为⾼性能的基础通信组件,它本⾝提供了

TCP/UDP和HTTP协议栈。

⾮常⽅便定制和开发私有协议栈,账号登录服务器,地图服务器之间可以⽅便的通过Netty进⾏⾼性能的通信

⼤数据领域

经典的Hadoop的⾼性能通信和序列化组件Avro的RPC框架,默认采⽤Netty进⾏跨界点通信,它的NettyService基于Netty框架⼆次封

装实现

⼆、Netty⾼性能设计

2.1Netty线程模型

Netty主要基于主从Reactors多线程模型(参考:)(如下图)做了⼀定的修改,其中主从Reactor多线程模型有多个Reactor:

MainReactor和SubReactor:

MainReactor负责客户端的连接请求,并将请求转交给SubReactor

SubReactor负责相应通道的IO读写请求

⾮IO请求(具体逻辑处理)的任务则会直接写⼊队列,等待workerthreads进⾏处理

这⾥引⽤DougLee⼤神的Reactor介绍:⾥⾯关于主从Reactor多线程模型的图

特别说明的是:

虽然Netty的线程模型基于主从Reactor多线程,借⽤了MainReactor和SubReactor的结构,但是实际实现上,SubReactor和Worker线

程在同⼀个线程池中:

1EventLoopGroupbossGroup=newNioEventLoopGroup();

2EventLoopGroupworkerGroup=newNioEventLoopGroup();

3ServerBootstraprver=newServerBootstrap();

(bossGroup,workerGroup).channel()

上⾯代码中的bossGroup和workerGroup是Bootstrap构造⽅法中传⼊的两个对象,这两个group均是线程池

bossGroup线程池则只是在bind某个端⼝后,获得其中⼀个线程作为MainReactor,专门处理端⼝的accept事件,每个端⼝对应⼀个

boss线程

workerGroup线程池会被各个SubReactor和worker线程充分利⽤

2.2异步处理

异步的概念和同步相对。当⼀个异步过程调⽤发出后,调⽤者不能⽴刻得到结果。实际处理这个调⽤的部件在完成后,通过状态、通知

和回调来通知调⽤者。

Netty中的I/O操作是异步的,包括bind、write、connect等操作会简单的返回⼀个ChannelFuture,调⽤者并不能⽴刻获得结果,通过

Future-Listener机制,⽤户可以⽅便的主动获取或者通过通知机制获得IO操作结果。

当future对象刚刚创建时,处于⾮完成状态,调⽤者可以通过返回的ChannelFuture来获取操作执⾏的状态,注册监听函数来执⾏完成后

的操作,常见有如下:

通过isDone⽅法来判断当前操作是否完成

通过isSuccess⽅法来判断已完成的当前操作是否成功

通过getCau⽅法来获取已完成的当前操作失败的原因

通过isCancelled⽅法来判断已完成的当前操作是否被取消

通过addListener⽅法来注册监听器,当操作已完成(isDone⽅法返回完成),将会通知指定的监听器;如果future对象已完成,则理解通

知指定的监听器

例如下⾯的代码中绑定端⼝是异步操作,当绑定操作处理完,将会调⽤相应的监听器处理逻辑

(port).addListener(future->{

2if(ess()){

n(newDate()+":端⼝["+port+"]绑定成功!");

4}el{

n("端⼝["+port+"]绑定失败!");

6}

7});

相⽐传统阻塞I/O,执⾏I/O操作后线程会被阻塞住,直到操作完成;异步处理的好处是不会造成线程阻塞,线程在I/O操作期间可以执⾏别

的程序,在⾼并发情形下会更稳定和更⾼的吞吐量。

三、Netty架构设计

前⾯介绍完Netty相关⼀些理论介绍,下⾯从功能特性、模块组件、运作过程来介绍Netty的架构设计

3.1功能特性

传输服务

⽀持BIO和NIO

容器集成

⽀持OSGI、JBossMC、Spring、Guice容器

协议⽀持

HTTP、Protobuf、⼆进制、⽂本、WebSocket等⼀系列常见协议都⽀持。

还⽀持通过实⾏编码解码逻辑来实现⾃定义协议

Core核⼼

可扩展事件模型、通⽤通信API、⽀持零拷贝的ByteBuf缓冲对象

3.2模块组件

Bootstrap、ServerBootstrap

Bootstrap意思是引导,⼀个Netty应⽤通常由⼀个Bootstrap开始,主要作⽤是配置整个Netty程序,串联各个组件,Netty中Bootstrap类

是客户端程序的启动引导类,ServerBootstrap是服务端启动引导类。

Future、ChannelFuture

正如前⾯介绍,在Netty中所有的IO操作都是异步的,不能⽴刻得知消息是否被正确处理,但是可以过⼀会等它执⾏完成或者直接注册

⼀个监听,具体的实现就是通过Future和ChannelFutures,他们可以注册⼀个监听,当操作执⾏成功或失败时监听会⾃动触发注册的监听事

件。

Channel

Netty⽹络通信的组件,能够⽤于执⾏⽹络I/O操作。

Channel为⽤户提供:

当前⽹络连接的通道的状态(例如是否打开?是否已连接?)

⽹络连接的配置参数(例如接收缓冲区⼤⼩)

提供异步的⽹络I/O操作(如建⽴连接,读写,绑定端⼝),异步调⽤意味着任何I/O调⽤都将⽴即返回,并且不保证在调⽤结束时所请求

的I/O操作已完成。调⽤⽴即返回⼀个ChannelFuture实例,通过注册监听器到ChannelFuture上,可以I/O操作成功、失败或取消时回

调通知调⽤⽅。

⽀持关联I/O操作与对应的处理程序

不同协议、不同的阻塞类型的连接都有不同的Channel类型与之对应,下⾯是⼀些常⽤的Channel类型

NioSocketChannel,异步的客户端TCPSocket连接

NioServerSocketChannel,异步的服务器端TCPSocket连接

NioDatagramChannel,异步的UDP连接

NioSctpChannel,异步的客户端Sctp连接

NioSctpServerChannel,异步的Sctp服务器端连接

这些通道涵盖了UDP和TCP⽹络IO以及⽂件IO.

Selector

Netty基于Selector对象实现I/O多路复⽤,通过Selector,⼀个线程可以监听多个连接的Channel事件,当向⼀个Selector中注册Channel

后,Selector内部的机制就可以⾃动不断地查询(lect)这些注册的Channel是否有已就绪的I/O事件(例如可读,可写,⽹络连接完成等),这样

程序就可以很简单地使⽤⼀个线程⾼效地管理多个Channel。

NioEventLoop

NioEventLoop中维护了⼀个线程和任务队列,⽀持异步提交执⾏任务,线程启动时会调⽤NioEventLoop的run⽅法,执⾏I/O任务和⾮

I/O任务:

I/O任务

即lectionKey中ready的事件,如accept、connect、read、write等,由processSelectedKeys⽅法触发。

⾮IO任务

添加到taskQueue中的任务,如register0、bind0等任务,由runAllTasks⽅法触发。

两种任务的执⾏时间⽐由变量ioRatio控制,默认为50,则表⽰允许⾮IO任务执⾏的时间与IO任务的执⾏时间相等。

NioEventLoopGroup

NioEventLoopGroup,主要管理eventLoop的⽣命周期,可以理解为⼀个线程池,内部维护了⼀组线程,每个线程(NioEventLoop)负责

处理多个Channel上的事件,⽽⼀个Channel只对应于⼀个线程。

ChannelHandler

ChannelHandler是⼀个接⼝,处理I/O事件或拦截I/O操作,并将其转发到其ChannelPipeline(业务处理链)中的下⼀个处理程序。

ChannelHandler本⾝并没有提供很多⽅法,因为这个接⼝有许多的⽅法需要实现,⽅便使⽤期间,可以继承它的⼦类:

ChannelInboundHandler⽤于处理⼊站I/O事件

ChannelOutboundHandler⽤于处理出站I/O操作

或者使⽤以下适配器类:

ChannelInboundHandlerAdapter⽤于处理⼊站I/O事件

ChannelOutboundHandlerAdapter⽤于处理出站I/O操作

ChannelDuplexHandler⽤于处理⼊站和出站事件

ChannelHandlerContext

保存Channel相关的所有上下⽂信息,同时关联⼀个ChannelHandler对象

ChannelPipline

保存ChannelHandler的List,⽤于处理或拦截Channel的⼊站事件和出站操作。ChannelPipeline实现了⼀种⾼级形式的拦截过滤器模

式,使⽤户可以完全控制事件的处理⽅式,以及Channel中各个的ChannelHandler如何相互交互。

下图引⽤Netty的Javadoc4.1中ChannelPipline的说明,描述了ChannelPipeline中ChannelHandler通常如何处理I/O事件。I/O事件由

ChannelInboundHandler或ChannelOutboundHandler处理,并通过调⽤ChannelHandlerContext中定义的事件传播⽅法(例如

annelRead(Object)和(Object))转发到其最近的处理程序。

1I/ORequest

2viaChannelor

3ChannelHandlerContext

4|

5+---------------------------------------------------+---------------+

6|ChannelPipeline||

7||/|

8|+---------------------++-----------+----------+|

9||InboundHandlerN||OutboundHandler1||

10|+----------+----------++-----------+----------+|

11|/|||

12|||/|

13|+----------+----------++-----------+----------+|

14||InboundHandlerN-1||OutboundHandler2||

15|+----------+----------++-----------+----------+|

16|/|.|

17|..|

18|_EVT()_EVT()|

19|[methodcall][methodcall]|

20|..|

21|.|/|

22|+----------+----------++-----------+----------+|

23||InboundHandler2||OutboundHandlerM-1||

24|+----------+----------++-----------+----------+|

25|/|||

26|||/|

27|+----------+----------++-----------+----------+|

28||InboundHandler1||OutboundHandlerM||

29|+----------+----------++-----------+----------+|

30|/|||

31+---------------+-----------------------------------+---------------+

32||/

33+---------------+-----------------------------------+---------------+

34||||

35|[()][()]|

36||

37|NettyInternalI/OThreads(TransportImplementation)|

38+-------------------------------------------------------------------+

⼊站事件由⾃下⽽上⽅向的⼊站处理程序处理,如图左侧所⽰。⼊站Handler处理程序通常处理由图底部的I/O线程⽣成的⼊站数据。

通常通过实际输⼊操作(例如(ByteBuffer))从远程读取⼊站数据。

出站事件由上下⽅向处理,如图右侧所⽰。出站Handler处理程序通常会⽣成或转换出站传输,例如write请求。I/O线程通常执⾏实际

的输出操作,例如(ByteBuffer)。

在Netty中每个Channel都有且仅有⼀个ChannelPipeline与之对应,它们的组成关系如下:

⼀个Channel包含了⼀个ChannelPipeline,⽽ChannelPipeline中⼜维护了⼀个由ChannelHandlerContext组成的双向链表,并且每

个ChannelHandlerContext中⼜关联着⼀个ChannelHandler。⼊站事件和出站事件在⼀个双向链表中,⼊站事件会从链表head往后传递到

最后⼀个⼊站的handler,出站事件会从链表tail往前传递到最前⼀个出站的handler,两种类型的handler互不⼲扰。

四、⼯作原理架构

初始化并启动Netty服务端过程如下:

1publicstaticvoidmain(String[]args){

2//创建mainReactor

3NioEventLoopGroupboosGroup=newNioEventLoopGroup();

4//创建⼯作线程组

5NioEventLoopGroupworkerGroup=newNioEventLoopGroup();

6

7finalServerBootstraprverBootstrap=newServerBootstrap();

8rverBootstrap

9//组装NioEventLoopGroup

(boosGroup,workerGroup)

11//设置channel类型为NIO类型

l()

13//设置连接配置参数

(_BACKLOG,1024)

ption(_KEEPALIVE,true)

ption(_NODELAY,true)

17//配置⼊站、出站事件handler

andler(newChannelInitializer(){

19@Override

20protectedvoidinitChannel(NioSocketChannelch){

21//配置⼊站、出站事件channel

ne().addLast(...);

ne().addLast(...);

24}

25});

26

27//绑定端⼝

28intport=8080;

(port).addListener(future->{

30if(ess()){

n(newDate()+":端⼝["+port+"]绑定成功!");

32}el{

n("端⼝["+port+"]绑定失败!");

34}

35});

36}

基本过程如下:

1初始化创建2个NioEventLoopGroup,其中boosGroup⽤于Accetpt连接建⽴事件并分发请求,workerGroup⽤于处理I/O读写事件和

业务逻辑

2基于ServerBootstrap(服务端启动引导类),配置EventLoopGroup、Channel类型,连接参数、配置⼊站、出站事件handler

3绑定端⼝,开始⼯作

结合上⾯的介绍的NettyReactor模型,介绍服务端Netty的⼯作架构图:

rver端包含1个BossNioEventLoopGroup和1个WorkerNioEventLoopGroup,NioEventLoopGroup相当于1个事件循环组,这个组⾥

包含多个事件循环NioEventLoop,每个NioEventLoop包含1个lector和1个事件循环线程。

每个BossNioEventLoop循环执⾏的任务包含3步:

1轮询accept事件

2处理acceptI/O事件,与Client建⽴连接,⽣成NioSocketChannel,并将NioSocketChannel注册到某个WorkerNioEventLoop的

Selector上

3处理任务队列中的任务,runAllTasks。任务队列中的任务包括⽤户调⽤e或schedule执⾏的任务,或者其它线程提

交到该eventloop的任务

每个WorkerNioEventLoop循环执⾏的任务包含3步:

1轮询read、write事件

2处理I/O事件,即read、write事件,在NioSocketChannel可读、可写事件发⽣时进⾏处理

3处理任务队列中的任务,runAllTasks

其中任务队列中的task有3种典型使⽤场景

1⽤户程序⾃定义的普通任务

l().eventLoop().execute(newRunnable(){

2@Override

3publicvoidrun(){

4//...

5}

6});

2⾮当前reactor线程调⽤channel的各种⽅法

例如在推送系统的业务线程⾥⾯,根据⽤户的标识,找到对应的channel引⽤,然后调⽤write类⽅法向该⽤户推送消息,就会进⼊到这

种场景。最终的write会提交到任务队列中后被异步消费。

3⽤户⾃定义定时任务

l().eventLoop().schedule(newRunnable(){

2@Override

3publicvoidrun(){

4

5}

6},60,S);

五、总结

现在稳定推荐使⽤的主流版本还是Netty4,Netty5中使⽤了ForkJoinPool,增加了代码的复杂度,但是对性能的改善却不明显,所以这

个版本不推荐使⽤,官⽹也没有提供下载链接。

Netty⼊门门槛相对较⾼,其实是因为这⽅⾯的资料较少,并不是因为他有多难,⼤家其实都可以像搞透Spring⼀样搞透Netty。在学

习之前,建议先理解透整个框架原理结构,运⾏过程,可以少⾛很多弯路。

作者:caison

链接:/p/6681bfa36c4f

来源:简书

著作权归作者所有。商业转载请联系作者获得授权,⾮商业转载请注明出处。

本文发布于:2022-12-28 13:38:39,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/fanwen/fan/90/46952.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

下一篇:kebab
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图