认识Netty

  • 时间:2021-03-20 18:44 作者:雨夜都行 来源: 阅读:26
  • 扫一扫,手机访问
摘要:Netty1.引言在单体应用下,由于不同业务是部署在同一个进程中的,当我们需要调用依赖业务的时候我们只要要用 本地方法调用 就可。随着业务的逐步庞大和访问量的增多等诸多问题的出现,我们会将应用按业务的不同或者者公共依赖等方式拆分成多个服务后构建出分布式或者者微服务的架构,这时不同的服务会部署在不同的进程
Netty

1.引言


在单体应用下,由于不同业务是部署在同一个进程中的,当我们需要调用依赖业务的时候我们只要要用 本地方法调用 就可。随着业务的逐步庞大和访问量的增多等诸多问题的出现,我们会将应用按业务的不同或者者公共依赖等方式拆分成多个服务后构建出分布式或者者微服务的架构,这时不同的服务会部署在不同的进程中,服务间就不能使用本地方法进行依赖服务的调用了。 那么服务间如何调用就成为了服务拆分后需要处理的公共关注点问题之一。为了区分本地方法调用,我们也将服务间的调用称为 远程方法调用
服务间的远程方法调用,顾名思义其中 服务间的远程调用 涉及了一个做为用户端,一个做为服务端,以及用户端和服务端之间的交互。为理解决服务间如何调用,我们就需要处理好几个问题:如何建立服务间的网络连接?连接建立好了数据如何传输?接收方如何解决接收到的数据?当我们能够处理好这几个问题时,服务间调用的问题也就基本上处理了。

2.如何处理这些问题即引出Netty


在引言中有提到 服务间的远程调用 面临的问题,我们在处理这类问题时有两种思路:自己搭建一套网络通信方案、使用开源项目。我会根据这两种思路进行分别详情。
首先先说自己搭建一套网络通信方案,做为一名JAVA工程师,第一种方案:我们会使用一对Socket套接字来实现基于同步阻塞式的IO「也称之为BIO」下面这张图是使用BIO来实现网络通信的简图,从图中可以看出之所以称为同步阻塞,其中同步的点在于:用户端或者者服务端在获取数据时需要主动进行获取,阻塞的点在于:服务端需要阻塞等待用户端发起连接,需要阻塞等待用户端发送数据。

BIO

第二种方案:使用JDK的NIO来实现基于同步非阻塞式的IO「也称之为NIO」用来解决用户端和服务端连接的建立和交互。JDK的NIO提供了三大组件:Buffer、Channel、Selector来实现IO多路复用「一个线程解决多个用户端连接和请求」的能力。下面这张图是使用NIO来实现网络通信的简图,我们把主要关注点关注在Server端。从图中可以看出与同步阻塞的区别:服务端不需要在阻塞等待用户端发起连接和发送数据了,而是非阻塞事件驱动的方式通过监听一个一个的事件来解决用户端的请求。另外一方面可以看出NIO模型相较于BIO会复杂许多。

NIO

第三种方案:使用JDK的AIO来实现基于异步非阻塞式的IO,这种方式不是本文的重点,后文也不在阐述,感兴趣的同学可以自行百度。
再说后者使用开源项目,目前市面上主要流行的通信框架有:Netty、Mina。Mina和Netty的作者实际上是同一个人,Mina不是本文详情的重点,感兴趣的同学可以自行百度。在上文中详情了最原始的JDK实现自己的网络通行框架,终于引出本文的主题 Netty ,后面我将会从以下几个方面详情Netty:Netty是什么?Netty能做什么?Netty去哪下?Netty怎样玩?Netty相关组件有哪些?Dubbo是如何使用Netty的?那么接下来让我们直接进入主题吧。

3.Netty入门

3.1.Netty是什么


这里我用Netty官方的形容来简单的说明:Netty是一个基于JAVA的NIO的通信框架并提供了异步、事件驱动、多协议支持等能力,可用于快速、简单地开发网络应用程序「包括用户端和服务端」

3.2.Netty能做什么


正如前面所谈及为理解决服务间可以相互调用,我们需要构建服务间的相互通信。我们完全可以用Netty来实现服务间的连接建立与通信。目前在朴朴使用的技术栈:Dubbo、ES、Redisson、Spark也使用了Netty做为底层的通信框架,并且Netty也是Dubbo默认的底层通信框架

3.3.Netty从哪下


从官方的角度,有三种方式可以去理解Netty和下载Netty相关的源码:

  • 官网:Netty。

官网也提供了相关的客户指南可作为入门文档:User Guide

  • github:Netty
  • 书籍:《Netty IN ACTION》由Netty主要开发者编写

Netty目前的主要版本系列是4.1.X,我们关注这个系列就可。3.X和4.1.X有很大区别,就连包名都完全不一样了,5.X已被Netty的相关开发人员放弃了,感兴趣的同学可以自行百度理解具体起因。

3.4.Netty怎样玩


在详情这章内容前,我们有必要先来理解一下Netty的架构图,以便后续我们更好的了解Netty的相关内容。下图就是用Netty来实现服务端的架构图(摘自百度图库)。我们先关注一下图中的重点字眼SelectoracceptselectchannelSelectedKeyreadwrite,我们对这些字眼是不是有似曾相识的感觉「印象模糊的同学可以回到2.中形容的第二种方案」。下面我会结合这张图与对应的代码对Netty的交互流程简单浅显的详情:

Netty架构图

Netty架构图对应的简单Netty代码如下,代码中涉及的类可以和上图中的关键字眼相对应起来,便于了解:

public void start() throws Exception {    NioEventLoopGroup bossGroup = new NioEventLoopGroup();    NioEventLoopGroup workerGroup = new NioEventLoopGroup();    try {        ServerBootstrap bootstrap = new ServerBootstrap();        bootstrap.group(bossGroup, workerGroup)                .channel(NioServerSocketChannel.class)                        .localAddress(new InetSocketAddress(8080))                    .childHandler(new ChannelInitializer<SocketChannel>() {                     @Override                    public void initChannel(SocketChannel ch)                            throws Exception {                        ch.pipeline().addLast(                                new EchoServerHandler());                    }                });        ChannelFuture f = bootstrap.bind().sync();        f.channel().closeFuture().sync();                } finally {        workerGroup.shutdownGracefully().sync();                    bossGroup.shutdownGracefully().sync();    }}
  1. 首先服务端启动后会创立两个类分别是BossGroup和WorkerGroup,我们先把它们了解为两个不同的线程池就可,只不过线程池中的线程是用来解决网络连接和读写请求的,不过还是需要额外提一下,BossGroup只负责解决用户端连接请求,WorkerGroup负责解决用户端的读写请求
  2. 创立一个服务端的引导启动类ServerBootStrap,并通过构建者模式往其中填充少量属性,重点关注Channel和ChannelHandler,因为是服务端并且我们使用的是NIO的通信方式所以我们使用的是NIOServerSocketChannel「假如想使用BIO的方式可修改为OIOServerSocketChannel,BIO不是本文的重点不在赘述」。在ChannelHandler中我们获取到了SocketChannel,并进一步通过SocketChannel获取到了它的Pipeline,并向其中增加了一个EchoServerHandler「用来解决实际业务的代码」,这段代码会在有新连接建立时进行回调的,我们有个印象就好
  3. 在创立好ServerBootStrap后,我们调用了bind方法,bind方法实际上会去做很多事情,我们只要要知道:将服务端绑定好端口,并将BossGroup线程池中的线程用来监听连接
  4. 后续Client发起连接后,Netty服务端会在BossGroup中的某一个线程的Select通过轮询出该连接请求后,并获取到用户端连接SocketChannel,往WorkerGroup中的某一线程的Selector注册read事件,用于后续该线程轮询并解决该用户端的写请求
  5. WorkerGroup中的某一线程接收到了用户端的写请求时,该线程会轮询到该事件,并将事件分发到与当前用户端连接SocketChannel关联的Pipeline中,Pipeline存储这一个一个的ChannelHandler,写请求事件会在Pipeline中的ChannelHandler向火炬一样向后流转并解决,这里用到了责任链的模式

其中3-5是Netty服务端解决数据的简单流程,我们可以结合2.第三种方案Netty架构图进行更深入的了解
注意由于Netty提供了异步的能力,但是有些情况下我们又不得不异步转同步等待。在上面代码中我们可以看到了bind的方法是异步的,我们需要主动调用sync,假如我们在这里没有调用sync的话,会导致后面的代码出错,异常退出程序的

3.5.Netty相关组件


在对Netty的整体架构有了肯定的理解之后,现在我对架构图中涉及的少量重点关键字眼进行逐个简单详情,希望大家可以对Netty相关组件有更深的认识以便后续更好的学习,那下面让我们开始吧:

  1. NioEventLoopGroup
    内部维护了一组线程「可以称之为IO线程」,默认线程个数为当前机器CPU核数*2。每一个线程会绑定一个Selector了解为就是JDK的NIO中的Selector选择器,用来轮询发生的事件
  2. Channel
    可以分为ServerSocketChannel和SocketChannel,在Netty中作为服务端使用的ServerSocketChannel会与BossGroup进行绑定,并用来接收用户端的连接。服务端接收到用户端的每一个连接都是一个SocketChannel,每一个SocketChannel会交给WorkGroup中的线程进行绑定
  3. Pipeline
    每一个SocketChannel都会有一个Pipeline「也称为ChannelPipeline」。WorkerGroup中轮询到的SocketChannel的某个事件会交由Pipeline进行解决
  4. ChannelHandler
    Pipiline可以看作为一个链表,链表中存储的数据阈实际上是一个一个的ChannelHandler「实际会封装成ChannelHandlerContext」,在Pipeline接收到WorkerGroup传递过来的事件后会通过内部存储的一个一个ChannelHandler向后传递,直到数据被ChannelHandler中的方法进行解决。我们通常所说的Netty是基于事件驱动的模型,实际上就是当事件发生后,ChannelHandler中的方法会被回调进行数据的解决,举个例子:ChannelHandler中的ChannelRead方法就是用来解决服务端接收到的用户端发送的数据

这里需要单独说明一下Pipeline中数据流向的相关问题,这里我都是针对服务端对象来说的:

  1. 从用户端向服务端发送的事件:比方连接请求、写请求我们可以称之为入站事件
  2. 从服务端向用户端发送的数据的过程,我们称之为出站事件

详情完组件后,现在我把它们之间的关系关联起来,可以得到下面这张图:


Netty的组件关系

3.6.在Dubbo中是如何使用Netty的


通过Netty的相关形容,想必大家对Netty的事件轮询,以及Netty是如何解决事件的机制有了肯定认识。在这里我还想在提一点Netty上使用的注意事项:经常可以听到不要在ChannelHandler中做少量耗时的事情。那么是为什么?究其起因,在上面形容Netty组件的过程中有说道,当NioEventLoopGroup中的线程「即IO线程」轮询到事件后,会将事件派发给Pipeline中的ChannelHandler进行解决,此时的解决还是在IO线程上进行,假如这个解决需要非常耗时,势必会影响IO线程轮询下一个事件。此时可以考虑转异步线程池中的线程去解决,我们也将这个异步线程池称之为业务线程
目前朴朴在服务间的调用使用的是Dubbo框架,针对上述的形容,大家能否已经联想到了Dubbo框架中是如何处理上面这个问题的呢。我在这里给个提醒点:在Dubbo中提出了多种的线程模型可供选择,以及提供了对应的业务线程池来处理这里提到的Netty注意事项。对Dubbo这块内容想更深入理解的同学可以自行百度。

4.总结


本编文章从服务化的拆分导致的业务间调用的差异开始,并引出这种差异所带来的问题。为理解决问题,我们理解了原生JDK的处理方案,以及使用开源项目来处理。并重点讲解了目前火热的通信框架Netty,当然随着Netty的发展,Netty的知识体系也是非常庞大的,本文篇幅有限不可能面面俱到,还有很多基础知识「比方Reactor模式、Netty的编解码,拆包粘包、客户事件等」在本文并没有谈及到,由于本文意在为大家在网络通信这一块扫盲,我也非常期待后续能够还有机会给大家进一步分享Netty。感兴趣的同学可以通过上述在3.3中提供的方式进一步理解Netty

  • 全部评论(0)
最新发布的资讯信息
【系统环境|windows】字节跳动前台面试题解析:盛最多水的容器(2021-03-20 21:27)
【系统环境|windows】DevOps敏捷60问,肯定有你想理解的问题(2021-03-20 21:27)
【系统环境|windows】字节跳动最爱考的前台面试题:JavaScript 基础(2021-03-20 21:27)
【系统环境|windows】JavaScript 的 switch 条件语句(2021-03-20 21:27)
【系统环境|windows】解决 XML 数据应用实践(2021-03-20 21:26)
【系统环境|windows】20个编写现代CSS代码的建议(2021-03-20 21:26)
【系统环境|windows】《vue 3.0探险记》- 运行报错:Error:To install them, you can run: npm install --save core-js/modules/es.arra...(2021-03-20 21:24)
【系统环境|windows】浅谈前台可视化编辑器的实现(2021-03-20 21:24)
【系统环境|windows】产品经理入门迁移学习指南(2021-03-20 21:23)
【系统环境|windows】初识webRTC(2021-03-20 21:23)
血鸟云
手机二维码手机访问领取大礼包
返回顶部