深入理解Netty以及为什么项目中要使用?(三)Netty框架的使用

2024-03-18 12:12

本文主要是介绍深入理解Netty以及为什么项目中要使用?(三)Netty框架的使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

为什么选择Netty

Netty其实就是一个高性能NIO框架,所以它是基于NIO基础上的封装,本质上是提供高性能网络IO通信的功能。

Netty提供了上述三种Reactor模型的支持,我们可以通过Netty封装好的API来快速完成不同Reactor模型的开发,这也是为什么大家都选择Netty的原因之一,除此之外,Netty相比于NIO原生API,它有以下特点:

  • 提供了高效的I/O模型、线程模型和时间处理机制

  • 提供了非常简单易用的API,相比NIO来说,针对基础的Channel、Selector、Sockets、Buffers等api提供了更高层次的封装,屏蔽了NIO的复杂性

  • 对数据协议和序列化提供了很好的支持

  • 稳定性,Netty修复了JDK NIO较多的问题,比如select空转导致的cpu消耗100%、TCP断线重连、keep-alive检测等问题。

  • 可扩展性在同类型的框架中都是做的非常好的,比如一个是可定制化的线程模型,用户可以在启动参数中选择Reactor模型、 可扩展的事件驱动模型,将业务和框架的关注点分离。

  • 性能层面的优化,作为网络通信框架,需要处理大量的网络请求,必然就面临网络对象需要创建和销毁的问题,这种对JVM的GC来说不是很友好,为了降低JVM垃圾回收的压力,引入了两种优化机制

    • 对象池复用,

    • 零拷贝技术

Netty的基本使用

添加jar包依赖

使用4.1.66版本

<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId>
</dependency>

创建Netty Server服务

大部分场景中,我们使用的主从多线程Reactor模型,Boss线程是主Reactor,Worker是从Reactor。他们分别使用不同的NioEventLoopGroup

主Reactor负责处理Accept,然后把Channel注册到从Reactor,从Reactor主要负责Channel生命周期内的所有I/O事件。

public class NettyBasicServerExample {public void bind(int port){// 我们要创建两个EventLoopGroup,// 一个是boss专门用来接收连接,可以理解为处理accept事件,// 另一个是worker,可以关注除了accept之外的其它事件,处理子任务。//上面注意,boss线程一般设置一个线程,设置多个也只会用到一个,而且多个目前没有应用场景,// worker线程通常要根据服务器调优,如果不写默认就是cpu的两倍。EventLoopGroup bossGroup=new NioEventLoopGroup();EventLoopGroup workerGroup=new NioEventLoopGroup();try {//服务端要启动,需要创建ServerBootStrap,// 在这里面netty把nio的模板式的代码都给封装好了ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workerGroup) //配置boss和worker线程//配置Server的通道,相当于NIO中的ServerSocketChannel.channel(NioServerSocketChannel.class)//childHandler表示给worker那些线程配置了一个处理器,// 配置初始化channel,也就是给worker线程配置对应的handler,当收到客户端的请求时,分配给指定的handler处理.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new NormalMessageHandler()); //添加handler,也就是具体的IO事件处理器}});//由于默认情况下是NIO异步非阻塞,所以绑定端口后,通过sync()方法阻塞直到连接建立//绑定端口并同步等待客户端连接(sync方法会阻塞,直到整个启动过程完成)ChannelFuture channelFuture=bootstrap.bind(port).sync();System.out.println("Netty Server Started,Listening on :"+port);//等待服务端监听端口关闭channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();} finally {//释放线程资源bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}public static void main(String[] args) {new NettyBasicServerExample().bind(8080);}
}

上述代码说明如下:

  • EventLoopGroup,定义线程组,相当于我们之前在写NIO代码时定义的线程。这里定义了两个线程组分别是boss线程和worker线程,boss线程负责接收连接,worker线程负责处理IO事件。boss线程一般设置一个线程,设置多个也只会用到一个,而且多个目前没有应用场景。而worker线程通常要根据服务器调优,如果不写默认就是cpu的两倍。

  • ServerBootstrap,服务端要启动,需要创建ServerBootStrap,在这里面netty把nio的模板式的代码都给封装好了。

  • ChannelOption.SO_BACKLOG

设置Channel类型

NIO模型是Netty中最成熟也是被广泛引用的模型,因此在使用Netty的时候,我们会采用NioServerSocketChannel作为Channel类型。

bootstrap.channel(NioServerSocketChannel.class);

除了NioServerSocketChannel以外,还提供了

  • EpollServerSocketChannel,epoll模型只有在linux kernel 2.6以上才能支持,在windows和mac都是不支持的,如果设置Epoll在window环境下运行会报错。

  • OioServerSocketChannel,用于服务端阻塞地接收TCP连接

  • KQueueServerSocketChannel,kqueue模型,是Unix中比较高效的IO复用技术,常见的IO复用技术有select, poll, epoll以及kqueue等等。其中epoll为Linux独占,而kqueue则在许多UNIX系统上存在。

注册ChannelHandler

在Netty中可以通过ChannelPipeline注册多个ChannelHandler,该handler就是给到worker线程执行的处理器,当IO事件就绪时,会根据这里配置的Handler进行调用。

这里可以注册多个ChannelHandler,每个ChannelHandler各司其职,比如做编码和解码的handler,心跳机制的handler,消息处理的handler等。这样可以实现代码的最大化复用。

.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new NormalMessageHandler());}
});

ServerBootstrap中的childHandler方法需要注册一个ChannelHandler,这里配置了一个ChannelInitializer的实现类,通过实例化ChannelInitializer来配置初始化Channel。

当收到IO事件后,这个数据会在这多个handler中进行传播。上述代码中配置了一个NormalMessageHandler,用来接收客户端消息并输出。

绑定端口

完成Netty的基本配置后,通过bind()方法真正触发启动,而sync()方法会阻塞,直到整个启动过程完成。

ChannelFuture channelFuture=bootstrap.bind(port).sync();

NormalMessageHandler

ServerHandler继承了ChannelInboundHandlerAdapter,这是netty中的一个事件处理器,netty中的处理器分为Inbound(进站)和Outbound(出站)处理器,后面会详细介绍。

public class NormalMessageHandler extends ChannelInboundHandlerAdapter {//channelReadComplete方法表示消息读完了的处理,writeAndFlush方法表示写入并发送消息@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {//这里的逻辑就是所有的消息读取完毕了,在统一写回到客户端。Unpooled.EMPTY_BUFFER表示空消息,addListener(ChannelFutureListener.CLOSE)表示写完后,就关闭连接ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);}//exceptionCaught方法就是发生异常的处理@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}//channelRead方法表示读到消息以后如何处理,这里我们把消息打印出来@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf in=(ByteBuf) msg;byte[] req=new byte[in.readableBytes()];in.readBytes(req); //把数据读到byte数组中String body=new String(req,"UTF-8");System.out.println("服务器端收到消息:"+body);//写回数据ByteBuf resp=Unpooled.copiedBuffer(("receive message:"+body+"").getBytes());ctx.write(resp);//ctx.write表示把消息再发送回客户端,但是仅仅是写到缓冲区,没有发送,flush才会真正写到网络上去}
}

通过上述代码发现,我们只需要通过极少的代码就完成了NIO服务端的开发,相比传统的NIO原生类库的服务端,代码量大大减少,开发难度也大幅度降低。

测试:浏览器输入http://localhost:8080/login

Netty和NIO的api对应

TransportChannel ----对应NIO中的channel

EventLoop---- 对应于NIO中的while循环

EventLoopGroup: 多个EventLoop,就是事件循环

ChannelHandler和ChannelPipeline---对应于NIO中的客户逻辑实现handleRead/handleWrite(interceptor pattern)

ByteBuf---- 对应于NIO 中的ByteBuffer

Bootstrap 和 ServerBootstrap ---对应NIO中的Selector、ServerSocketChannel等的创建、配置、启动等

这篇关于深入理解Netty以及为什么项目中要使用?(三)Netty框架的使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/822299

相关文章

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Python内置函数之classmethod函数使用详解

《Python内置函数之classmethod函数使用详解》:本文主要介绍Python内置函数之classmethod函数使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 类方法定义与基本语法2. 类方法 vs 实例方法 vs 静态方法3. 核心特性与用法(1编程客

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

ModelMapper基本使用和常见场景示例详解

《ModelMapper基本使用和常见场景示例详解》ModelMapper是Java对象映射库,支持自动映射、自定义规则、集合转换及高级配置(如匹配策略、转换器),可集成SpringBoot,减少样板... 目录1. 添加依赖2. 基本用法示例:简单对象映射3. 自定义映射规则4. 集合映射5. 高级配置匹

Spring 框架之Springfox使用详解

《Spring框架之Springfox使用详解》Springfox是Spring框架的API文档工具,集成Swagger规范,自动生成文档并支持多语言/版本,模块化设计便于扩展,但存在版本兼容性、性... 目录核心功能工作原理模块化设计使用示例注意事项优缺点优点缺点总结适用场景建议总结Springfox 是