Netty框架简述

article/2025/9/21 11:32:26

Netty是什么?

       Netty 是一个广泛使用的 Java 网络编程框架,Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。Netty 的内部实现时很复杂的,但是 Netty 提供了简单易用的 api 从网络处理代码中解耦业务逻辑。 Netty 是完全基于 NIO 实现的,实际上相当于NIO+多线程,所以整个 Netty 都是异步的。 简单点说就是Netty提供了一个简单,间接的方法来操作网络之间的通讯。

Netty的优势是什么?

  1. 并发高

  2. 传输快

  3. 封装好

为什么并发高?

     Netty是基于NIO实现的 ,相较于BIO(单个线程控制单个连接),它的并发性得到了很大的提高(原因在于selector的出现,使得单个线程可以控制多个连接),从下图可以明显看出NIO与BIO的区别:

                                  BIO处理流程

                           NIO处理流程

在BIO中,等待客户端发数据这个过程是阻塞的,这样就造成了一个线程只能处理一个请求的情况,而机器能支持的最大线程数是有限的,这就是为什么BIO不能支持高并发的原因。

而NIO中,当一个Socket建立好之后,Thread并不会阻塞去接受这个Socket,而是将这个请求交给Selector,Selector会不断的去遍历所有的Socket,一旦有一个Socket建立完成,他会通知Thread,然后Thread处理完数据再返回给客户端——这个过程是阻塞的,这样就能让一个Thread处理更多的请求了。

为什么传输快?

Netty的传输快其实也是依赖了NIO的一个特性——零拷贝。我们知道,Java的内存有堆内存、栈内存和字符串常量池等等,其中堆内存是占用内存空间最大的一块,也是Java对象存放的地方,一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说一个数据会被拷贝两次才能到达他的的终点,如果数据量大,就会造成不必要的资源浪费。

Netty针对这种情况,使用了NIO中的另一大特性——零拷贝,当他需要接收数据的时候,他会在堆内存之外开辟一块内存,数据就直接从IO读到了那块内存中去,在netty里面通过ByteBuf可以直接对这些数据进行直接操作,从而加快了传输速度。

 

   关于零拷贝的内容会在下篇博文介绍。

为什么封装好?

     Netty基于NIO,同时也改进了NIO中的一些问题,将改进方法进行了封装,使用户不用在意底层操作,操作更加简便。详细会在Netty的源码分析进行介绍。

基本组件是什么? 

  • Channel  ----Socket

  • EventLoop ----控制流,多线程处理,并发;

  • ChannelHandler和ChannelPipeline

  • Bootstrap 和 ServerBootstrap

Channel 接口

     基本的I/O操作,在基于java 的网络编程中,其基本的构造是 Socket,在jdk中channel是通讯载体,在netty中channel被赋予了更多的功能。

EventLoop 接口

      EventLoop 是用来处理连接的生命周期中所发生的事情,EventLoop, channel, Thread 以及 EventLoopGroup 之间的关系如下图:

 

这几个组件之间的关系总结下就是:

  • 一个 EventLoopGroup 包含多个 EventLoop

  • 一个 EventLoop 在他的生命周期中只和一个 Thread 绑定

  • 所有的 EventLoop 处理的 I/O 事件都将在专有的 Thread 上处理

  • 一个 Channel 在他的生命周期中只会注册一个 EventLoop

  • 一个 EventLoop 会被分配给多个 Channel; 

ChannelHandler 接口

     ChannelHandler 充当了所有处理入站和出站数据的应用程序逻辑的容器,ChannelHandler 的生命周期主要指的是 handler 添加到 ChannelPipeline 中,handler 从pipeline 中移除 。ChannelHandler 主要是用来用来用户入站出站的工具,netty 中提供了一些开箱即用的处理器,本文只是介绍常见组件,以及他们之间的关系。

ChannelPipeline 接口

      ChannelPipeline 为 ChannelHandler 链提供了容器,当 channel 创建时,就会被自动分配到它专属的 ChannelPipeline ,这个关联是永久性的。

     ChannelHandler 添加到 ChannlePipeline 的过程:

  • 一个 ChannelInitializer 的实现被注册到 ServerBootstrop 当中

  • 当 ChannelInitializer.initChannel() 方法被调用时,ChannelInitializer 将在 ChannelPipeline 中安装一组自定义的 ChannelHandler

  • ChannelInitializer 将他自己从 ChannelPipeline 中移除;

       当 ChannelHandler 被添加到 ChannelPipeline 时,会被分配一个 ChannelHandlerContext ,代表的是 ChannelHandler 和 ChannelPipeline 之间的绑定。

Netty 中发送消息有两种方式,可以直接写入到 Channel 中,也可以写入到和 ChannelHandler 绑定的 ChannelHandlerContext 中。前者会使消息从 ChannelPipeline 当中尾部开始移动,后者会导致消息从 ChannelPipeline 中的下一个 ChannelHandler 中移动。

ChannelHandlerContext 接口

      ChannelHandlerContext 主要是用来管理它所关联的 ChannelHandler 和在同一个 ChannlePipeline 中其他的 ChannelHandler 之间的交互。

Bootstrap 和 ServerBootstrap(引导类)

        Bootstrap 和 ServerBootstrap 这两个引导类分别是用来处理客户端和服务端的信息,服务器端的引导一个父 Channel 用来接收客户端的连接,一个子 Channel 用来处理客户端和服务器端之间的通信,客户端则只需要一个单独的、没有父 Channel 的 Channel 来去处理所有的网络交互(或者是无连接的传输协议,如 UDP)

这里可以大概先总结一下,之间的关系:

  • 一个EventLoopGroup当中包含多个EventLoop

  • 一个EventLoop在它的整个生命周期中只于一个thread进行绑定

  • 所有由EventLoop所处理的各种I/O操作都将在它关联的Thread上进行处理

  • 一个Channel在它的生命周期中只会注册在一个EventLoop上

  • 一个EvenLoop在运行当中,会被分配给多个Channel

Netty中的责任链模式

责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。


  • 从击鼓传花谈起

  击鼓传花是一种热闹而又紧张的饮酒游戏。在酒宴上宾客依次坐定位置,由一人击鼓,击鼓的地方与传花的地方是分开的,以示公正。开始击鼓时,花束就开始依次传递,鼓声一落,如果花束在某人手中,则该人就得饮酒。

  比如说,贾母、贾赦、贾政、贾宝玉和贾环是五个参加击鼓传花游戏的传花者,他们组成一个环链。击鼓者将花传给贾母,开始传花游戏。花由贾母传给贾赦,由贾赦传给贾政,由贾政传给贾宝玉,又贾宝玉传给贾环,由贾环传回给贾母,如此往复,如下图所示。当鼓声停止时,手中有花的人就得执行酒令。

  击鼓传花便是责任链模式的应用。责任链可能是一条直线、一个环链或者一个树结构的一部分。

  • 责任链模式的结构

  下面使用了一个责任链模式的最简单的实现。

责任链模式涉及到的角色如下所示:

  ●  抽象处理者(Handler)角色:定义出一个处理请求的接口。如果需要,接口可以定义 出一个方法以设定和返回对下家的引用。这个角色通常由一个Java抽象类或者Java接口实现。上图中Handler类的聚合关系给出了具体子类对下家的引用,抽象方法handleRequest()规范了子类处理请求的操作。

  ●  具体处理者(ConcreteHandler)角色:具体处理者接到请求后,可以选择将请求处理掉,或者将请求传给下家。由于具体处理者持有对下家的引用,因此,如果需要,具体处理者可以访问下家。

  • Netty中的责任链

    数据传输流,与channel相关的概念有以下四个,上一张图让你了解netty里面的Channel。

     

    • Channel,表示一个连接,可以理解为每一个请求,就是一个Channel。

    • ChannelHandler,核心处理业务就在这里,用于处理业务请求。

    • ChannelHandlerContext,用于传输业务数据。

    • ChannelPipeline,用于保存处理过程需要用到的ChannelHandler和ChannelHandlerContext。

Netty将Channel的数据管道抽象为ChannelPipeline,消息在ChannelPipline中流动和传递。ChannelPipeline持有IO事件拦截器ChannelHandler的链表,由ChannelHandler对IO事件进行拦截和处理,可以方便的新增和删除ChannelHandler来实现不同的业务逻辑定制,不必对已有的ChannelHandler进行修改,这个开放闭合原则的很好体现。 

      ChannlePipeline的事件事件处理流程,如下图

  1. 底层Socket读取bytebuf触发ChannelRead事件(Inbound 事件),由NioEventLoop调用ChannelPipeline的fireChannelRead方法;
  2. 消息被ChannelPipeline中的ChannelHandlerContext传递,依次被各个ChannelHandler处理;
  3. 当有写出的需求(Outbound 事件),调用ChannelHandlerContext write方法,消息再通过ChannelHandlerContext反向传递通过各个ChannelHandler处理。当然各个ChannelHadler可以通过定制只对自己感兴趣的消息进行处理,其余跳过。

Netty线程模型

        Netty通过Reactor模型基于多路复用器接收并处理用户请求,内部实现了两个线程池,boss线程池和work线程池,其中boss线程池的线程负责处理请求的accept事件,当接收到accept事件的请求时,把对应的socket封装到一个NioSocketChannel中,并交给work线程池,其中work线程池负责请求的read和write事件,由对应的Handler处理。

单线程模型:所有I/O操作都由一个线程完成,即多路复用、事件分发和处理都是在一个Reactor线程上完成的。既要接收客户端的连接请求,向服务端发起连接,又要发送/读取请求或应答/响应消息。一个NIO 线程同时处理成百上千的链路,性能上无法支撑,速度慢,若线程进入死循环,整个程序不可用,对于高负载、大并发的应用场景不合适。

多线程模型:有一个NIO 线程(Acceptor) 只负责监听服务端,接收客户端的TCP 连接请求;NIO 线程池负责网络IO 的操作,即消息的读取、解码、编码和发送;1 个NIO 线程可以同时处理N 条链路,但是1 个链路只对应1 个NIO 线程,这是为了防止发生并发操作问题。但在并发百万客户端连接或需要安全认证时,一个Acceptor 线程可能会存在性能不足问题。

主从多线程模型:Acceptor 线程用于绑定监听端口,接收客户端连接,将SocketChannel 从主线程池的Reactor 线程的多路复用器上移除,重新注册到Sub 线程池的线程上,用于处理I/O 的读写等操作,从而保证mainReactor只负责接入认证、握手等操作;

利用主从 NIO 线程模型,可以解决 1 个服务端监听线程无法有效处理所有客户端连接的性能不足问题。因此,在 Netty 的官方 demo 中,推荐使用该线程模型。

事实上,Netty 的线程模型并非固定不变,通过在启动辅助类中创建不同的 EventLoopGroup 实例并通过适当的参数配置,就可以支持上述三种 Reactor 线程模型。正是因为 Netty 对 Reactor 线程模型的支持提供了灵活的定制能力,所以可以满足不同业务场景的性能诉求。

Netty编程实现服务端和客户端

注意:在使用Netty前先要添加Netty的jar包

NettyServer

public class NettyServer {public static void main(String[] args) {NioEventLoopGroup boss = new NioEventLoopGroup(1);//主事件组作用是接收新用户连接,并交给工作事件组NioEventLoopGroup worker = new NioEventLoopGroup();//工作事件组的作用是接收主事件组提交的用户连接并做相应处理try {//服务端启动相关辅助类,作用是将接收连接和用户逻辑处理相关的配置进行初始化ServerBootstrap serverBootstrap = new ServerBootstrap()//将主/工作事件组进行配置.group(boss, worker)//设置主事件组处理的通道类型.channel(NioServerSocketChannel.class)//配置工作事件组相关的Handler.childHandler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {ChannelPipeline pipeline = nioSocketChannel.pipeline();pipeline.addLast(new NettyServerHandler());pipeline.addLast(new StringDecoder());//定义接收类型为将字符串转为ByteBufpipeline.addLast(new StringEncoder());//定义发送类型为将ByteBuf转为字符串}});//同步阻塞绑定端口,真正启动服务器ChannelFuture sync = serverBootstrap.bind(1234).sync();System.out.println("服务端绑定端口成功且启动:");//同步阻塞关闭服务端sync.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();}finally {//关闭主事件组和工作事件组boss.shutdownGracefully();worker.shutdownGracefully();}}
}

NettyServerHandler

//自定义实现Handler,用于对客户端发送数据进行读取
public class NettyServerHandler extends SimpleChannelInboundHandler {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf)msg;byte[] bytes = new byte[buf.readableBytes()];buf.readBytes(bytes);String s = new String(bytes);System.out.println("server收到消息:"+s);//给客户端回复消息ByteBuf res = Unpooled.copiedBuffer(("server确认收到消息:"+s).getBytes());ctx.channel().writeAndFlush(res);}//用户新连接消息:accept事件连接上触发该操作@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {SocketAddress address = ctx.channel().remoteAddress();System.out.println(address+"上线了...");}//用户下线@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {SocketAddress address = ctx.channel().remoteAddress();System.out.println(address+"下线了...");}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {super.exceptionCaught(ctx, cause);}
}

NettyClient

public class NettyClient {public static void main(String[] args)  {NioEventLoopGroup loopGroup = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap().group(loopGroup).channel(NioSocketChannel.class).remoteAddress("127.0.0.1",1234).handler(new ChannelInitializer<NioSocketChannel>() {@Overrideprotected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {ChannelPipeline pipeline = nioSocketChannel.pipeline();pipeline.addLast(new NettyClientHandler());pipeline.addLast(new StringDecoder());pipeline.addLast(new StringEncoder());}});//同步阻塞连接服务端Channel channel = bootstrap.connect().sync().channel();//发消息String msg = new String("hello");channel.writeAndFlush(msg);//关闭通道channel.closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();}finally {//关闭事件组loopGroup.shutdownGracefully();}}
}

NettyClientHandler

public class NettyClientHandler extends SimpleChannelInboundHandler{@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf = (ByteBuf)msg;byte[] bytes = new byte[buf.readableBytes()];buf.readBytes(bytes);String s = new String(bytes);System.out.println("client接收消息:"+"\n"+s);}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {SocketAddress address = ctx.channel().remoteAddress();System.out.println(address+"正在连接...");}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {super.exceptionCaught(ctx, cause);}
}

运行结果:

server

client

注:若要实现客户端循环发送消息,服务端循环接收消息,只需要将NettyClient类中发消息的部分稍加改动即可。


http://chatgpt.dhexx.cn/article/isuq7AYJ.shtml

相关文章

Netty框架的基本使用

Netty概述 为什么使用Netty 前面了解过的NIO模型&#xff0c;它有可靠性高、吞吐量高的优点&#xff0c;但也存在编程复杂的问题&#xff0c;我们要掌握大量的API&#xff0c;如&#xff1a;各种Channel、Buffer、Selector&#xff0c;还要编程处理特殊情况&#xff0c;如&am…

初识Netty框架

总体概述 Netty作为一款网络通信框架&#xff0c;底层封装了NIO。我们在使用Netty时&#xff0c;无需再关注NIO细节。下图为Netty处理流程图&#xff1a; 应用程序中使用Netty作为网络通信框架后&#xff0c;会形成一条PipeLine链&#xff0c;PipeLine链上有一个一个的事件处…

【java网络编程】netty框架

一、简介 netty是一个高性能、异步事件驱动的NIO框架&#xff0c;它基于Java Nio提供的API实现&#xff0c;提供了对TCP、UDP和文件传输的支持。 二、Reactor模型 Reactor是一种并发处理客户端请求响应的事件驱动模型。服务端在接收到客户端请求后采用多路复用策略&#xff0…

Netty学习二:Netty整体框架

一、Netty的整体结构和源码结构 1. Core层 提供底层网络通信的通用抽象和实现&#xff0c;包括可扩展的事件模型、通用的通信API和支持零拷贝的ByteBuf等。 common模块是Netty的核心基础包&#xff0c;提供丰富的工具类&#xff0c;其他模块都需要依赖该模块。常用的包括&…

Netty框架

概述 Netty 是由 JBOSS 提供的一个 Java 开源框架。Netty 提供异步的、基于事件驱动的网络应用程序框架&#xff0c;用以快速开发高性能、高可靠性的网络 IO 程序。 Netty 是一个基于 NIO 的网络编程框架&#xff0c;使用 Netty 可以帮助你快速、简单的开发出一个网络应用&…

Netty框架基本介绍

NIO 1.概述&#xff1a;NIO全称java non-blocking IO &#xff0c;是指JDK1.4开始&#xff0c;java提供了一系列改进的输入/输出的新特性&#xff0c;被统称为NIO(即New IO )。新增了许多用于处理输入输出的类&#xff0c;这些类都被放在java.nio包及子包下&#xff0c;并且对j…

超详细Netty入门,看这篇就够了!

思维导图 前言 本文主要讲述Netty框架的一些特性以及重要组件&#xff0c;希望看完之后能对Netty框架有一个比较直观的感受&#xff0c;希望能帮助读者快速入门Netty&#xff0c;减少一些弯路。 一、Netty概述 官方的介绍&#xff1a; Netty is an asynchronous event-drive…

SPI通信协议详解(一)

SPI是一个同步的数据总线&#xff0c;也就是说它是用单独的数据线和一个单独的时钟信号来保证发送端和接收端的完美同步。 时钟是一个振荡信号&#xff0c;它告诉接收端在确切的时机对数据线上的信号进行采样。 产生时钟的一侧称为主机&#xff0c;另一侧称为从机。总是只有一个…

SPI协议的介绍

学习内容&#xff1a; 学习SPI协议记录 学习清单&#xff1a; 提示&#xff1a;这里可以添加要学的内容 例如&#xff1a; SPI协议的介绍三根线还是四根线两个概念四种模式SPI时序图SPI优缺点SPI和IIC的对比 学习详细内容&#xff1a; 1.SPI协议的介绍 SPI是串口外设接口…

SPI 通信协议

文章目录 【 1. 概述 】【 2. 原理 】1. 全双工特征下的传输特点2. 二线式、三线式的SPI3. SPI 模式CPOL(Clock Polarity)&#xff0c;时钟极性CPHA (Clock Phase)&#xff0c;时钟相位通信模式_时序 4. 通信过程5. 底层数据传输演示 【 3. SPI底层驱动 】 【 1. 概述 】 SPI …

SPI通信协议基础

文章目录 引言正文串行与并行通信SPI通信简介SPI如何运作&#xff1f;时钟从机选择多个从机MOSI和MISOSPI数据传输步骤 SPI的优缺点优点缺点 好文推荐参考 引言 当您将微控制器连接到传感器&#xff0c;显示器或其他模块时&#xff0c;您是否考虑过这两种设备如何相互通信&…

SPI、I2C、UART(即串口)三种串行总线详解

以下内容均来源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 参考博客 几个串口协议学习整理 UART IIC SPI_mainn的博客-CSDN博客 SPI、I2C、UART三种串行总线的原理、区别及应用_嵌入式Linux,的博客-CSDN博客 RS-232 和 UART 之间有什么区别&#xff1f; - 知乎…

uart、spi、i2c通信协议详解

文章目录 前言一、uart&#xff08;串口&#xff09;介绍使用rs232、rs485拓展 二、I2C介绍1.宏观流程2.时序上3.典型操作 三、spi&#xff08;串行外设接口&#xff09;介绍使用1.宏观上2.微观上 总结1.i2c和spi对比 前言 之前对于各种通信协议学习了就忘&#xff0c;学了就忘…

普通GPIO模拟SPI通信协议(软件SPI)

在工作中偶尔会遇到SPI不够用的情况&#xff0c;而我们又要去使用SPI通信协议&#xff0c;此时就需要我们自己去模拟SPI通信协议。我们知道SPI通信协议有四种模式&#xff0c;它们分别如下所示&#xff1a; 下面是我基于ATSAM4SD16B芯片在Atmel Studio上用普通GPIO模拟的SPI…

SPI 协议

一. 概念 1. 通常SPI通信要求4根线&#xff0c;分别是MOSI(mast output salve input), MISO, CLK, CS。 2. 当发送和接受数据的工作都准备好了&#xff0c;只要有时钟CLK,就发送数据&#xff0c;没有时钟就不发送&#xff0c;而且一个时钟周期发送一位(bit)数据&#xff0c;所…

SPI通信协议(SPI总线)学习

转载自&#xff1a;https://www.cnblogs.com/deng-tao/p/6004280.html 1、什么是SPI&#xff1f; SPI是串行外设接口(Serial Peripheral Interface)的缩写。是 Motorola 公司推出的一 种同步串行接口技术&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总…

SPI通信

1 SPI的简介及基本特点 1 SPI的简单介绍 最近工作中使用了SPI通信方式来做TM4C129和STM32之间的通信&#xff0c;为了更好地解决问题就学习了SPI原理的相关内容&#xff0c;完成了项目之后&#xff0c;也对这种通信方式有了较为深入的了解&#xff0c;现在来对SPI的使用方法进…

详解SPI通信协议

一、SPI简介 SPI&#xff0c;即Serial Peripheral Interface的英文缩写。从字面意思看就是串行外部设备接口&#xff0c;是一种全双工、高速、同步的通信总线。 SPI最早是摩托罗拉公司开发的全双工同步串行总线&#xff0c;用于微控制器&#xff08;MCU&#xff09;连接外部设…

stm32中常见的通信协议之SPI

目录 1.SPI总线2.SPI的寻址方式3.SPI的工作原理总结4.SPI的通讯过程5.SPI的极性和相位6.IIC和SPI的异同7.stm32中SPI配置中常用的寄存器8.stm32中相关库函数 1.SPI总线 SPI是串行外设接口的缩写&#xff0c;SPI是一种高速的、全双工、同步的串行通信协议&#xff1b;SPI采用主…

【通信协议】一文搞懂SPI

SPI总线简介 SPI(Serial Peripheral Interface)是 Motorola 公司推出的一种同步串行接口技术&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线。 接口定义 SPI接口共有4根信号线&#xff0c;分别是&#xff1a;片选信号、时钟信号、串行输出信号、串行…