网站开发兼容性,网站设计尺寸,网页微信版官网登录保存文件在哪里,能在家做的兼职的网站目录 Netty核心功能以及线程模型
Netty初探
Netty的使用场景#xff1a;
Netty通讯示例
Netty线程模型
Netty模块组件 Netty核心功能以及线程模型
Netty初探
NIO 的类库和 API 繁杂#xff0c; 使用麻烦#xff1a; 需要熟练掌握Selector、 ServerSocketChannel、 So…目录 Netty核心功能以及线程模型
Netty初探
Netty的使用场景
Netty通讯示例
Netty线程模型
Netty模块组件 Netty核心功能以及线程模型
Netty初探
NIO 的类库和 API 繁杂 使用麻烦 需要熟练掌握Selector、 ServerSocketChannel、 SocketChannel、 ByteBuffer等。
开发工作量和难度都非常大 例如客户端面临断线重连、 网络闪断、心跳处理、半包读写、 网络拥塞和异常流的处理等等。
Netty 对 JDK 自带的 NIO 的 API 进行了良好的封装解决了上述问题。且Netty拥有高性能、 吞吐量更高延迟更低减少资源消耗最小化不必要的内存复制等优点。
Netty 现在都在用的是4.x5.x版本已经废弃Netty 4.x 需要JDK 6以上版本支持
Netty的使用场景
1互联网行业在分布式系统中各个节点之间需要远程服务调用高性能的 RPC 框架必不可少Netty 作为异步高性能的通信框架往往作为基础通信组件被这些 RPC 框架使用。典型的应用有阿里分布式服务框架 Dubbo 的 RPC 框架使用 Dubbo 协议进行节点间通信Dubbo 协议默认使用 Netty 作为基础通信组件用于实现。各进程节点之间的内部通信。Rocketmq底层也是用的Netty作为基础通信组件。
2游戏行业无论是手游服务端还是大型的网络游戏Java 语言得到了越来越广泛的应用。Netty 作为高性能的基础通信组件它本身提供了 TCP/UDP 和 HTTP 协议栈。
3大数据领域经典的 Hadoop 的高性能通信和序列化组件 Avro 的 RPC 框架默认采用 Netty 进行跨界点通信它的 Netty Service 基于 Netty 框架二次封装实现。
Netty通讯示例
Netty的maven依赖
dependencygroupIdio.netty/groupIdartifactIdnetty-all/artifactIdversion4.1.35.Final/version
/dependency
Server端代码
/*** Description: TODO* Author: etcEriksen* Date: 2023/3/1**/
public class NettyServer {public static void main(String[] args) {//创建两个线程组bossGroup和workerGroup, 含有的子线程NioEventLoop的个数默认为cpu核数的两倍// bossGroup只是处理连接请求 ,真正的和客户端业务处理会交给workerGroup完成EventLoopGroup bossGroup new NioEventLoopGroup(1);EventLoopGroup workerGroup new NioEventLoopGroup();try {//创建服务端的启动对象ServerBootstrap bootstrap new ServerBootstrap();//使用链式编程来配置参数 设置两个线程组bootstrap.group(bossGroup,workerGroup)//使用NioServerSocketChannel作为服务器管道的实现.channel(NioServerSocketChannel.class)//初始化服务器连接队列的大小服务器端处理客户端连接请求是顺序处理的所以同一时间只能处理一个客户端连接//多个客户端同时来的时候服务端将不能处理的客户端连接请求放在队列中进行等待.option(ChannelOption.SO_BACKLOG,1024)//创建通道初始化对象设置初始化参数.childHandler(new ChannelInitializerSocketChannel() {Overrideprotected void initChannel(SocketChannel ch) throws Exception {//对workerGroup的SocketChannel设置处理器ch.pipeline().addLast(new NettyServerHandler());}}) ;System.out.println(netty server start ... );//绑定一个端口并且同步生成了一个ChannelFuture异步对象通过isDone()等方法可以进行判断异步事件的执行情况//启动服务器(并且绑定端口)bind是异步操作sync方法是等待异步操作执行完毕ChannelFuture cf bootstrap.bind(9000).sync();//给cf注册监听器监听我们关心的事件/*cf.addListener(new ChannelFutureListener() {Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (cf.isSuccess()) {System.out.println(监听端口9000成功);} else {System.out.println(监听端口9000失败);}}});*///对通道关闭进行监听closeFuture是异步操作监听通道关闭//通过sync方法进行同步等待通道关闭处理完毕这里会阻塞等待通道关闭完成cf.channel().closeFuture().sync() ;} catch (Exception e) {e.printStackTrace();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}}Server端处理器代码
/*** 自定义Handler处理器需要进行继承netty规定好的某一个HandlerAdpter*/
public class NettyServerHandler extends ChannelInboundHandlerAdapter {/*** 读取客户端发送的数据* param ctx 上下文对象含有通道channel管道pipeline* param msg 就是客户端发送的数据* throws Exception*/Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println(服务器读取线程 Thread.currentThread().getName());//Channel channel ctx.channel();//ChannelPipeline pipeline ctx.pipeline(); //本质是一个双向链接, 出站入站//将 msg 转成一个 ByteBuf类似NIO 的 ByteBufferByteBuf buf (ByteBuf) msg;System.out.println(客户端发送消息是: buf.toString(CharsetUtil.UTF_8));}/*** 数据读取完毕处理方法** param ctx* throws Exception*/Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ByteBuf buf Unpooled.copiedBuffer(HelloClient, CharsetUtil.UTF_8);ctx.writeAndFlush(buf);}/*** 处理异常, 一般是需要关闭通道** param ctx* param cause* throws Exception*/Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}}Client端代码
public class NettyClient {public static void main(String[] args) throws InterruptedException {//客户端需要一个事件循环组EventLoopGroup group new NioEventLoopGroup();try {//创建客户端启动对象//注意客户端使用的不是 ServerBootstrap 而是 BootstrapBootstrap bootstrap new Bootstrap();//设置相关参数bootstrap.group(group) //设置线程组.channel(NioSocketChannel.class) // 使用 NioSocketChannel 作为客户端的通道实现.handler(new ChannelInitializerSocketChannel() {Overrideprotected void initChannel(SocketChannel channel) throws Exception {//加入处理器channel.pipeline().addLast(new NettyClientHandler());}});System.out.println(netty client start);//启动客户端去连接服务器端ChannelFuture channelFuture bootstrap.connect(127.0.0.1, 9000).sync();//对关闭通道进行监听channelFuture.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}}Client端处理器代码
/*** Description: TODO* Author: etcEriksen* Date: 2023/3/1**/
public class NettyClientHandler extends ChannelInboundHandlerAdapter {/*** 当客户端连接服务器完成就会触发该方法** param ctx* throws Exception*/Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {ByteBuf buf Unpooled.copiedBuffer(HelloServer, CharsetUtil.UTF_8);ctx.writeAndFlush(buf);}//当通道有读取事件时会触发即服务端发送数据给客户端Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf buf (ByteBuf) msg;System.out.println(收到服务端的消息: buf.toString(CharsetUtil.UTF_8));System.out.println(服务端的地址 ctx.channel().remoteAddress());}Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}}
看完代码我们发现Netty框架的目标就是让你的业务逻辑从网络基础应用编码中分离出来让你可以专注业务的开发而不需写一大堆类似NIO的网络处理操作。
Netty线程模型 模型解释
1) Netty 抽象出两组线程池BossGroup和WorkerGroupBossGroup专门负责接收客户端的连接, WorkerGroup专门负责网络的读写
2) BossGroup和WorkerGroup类型都是NioEventLoopGroup
3) NioEventLoopGroup 相当于一个事件循环线程组, 这个组中含有多个事件循环线程 每一个事件循环线程是NioEventLoop
4) 每个NioEventLoop都有一个selector , 用于监听注册在其上的socketChannel的网络通讯
5) 每个Boss NioEventLoop线程内部循环执行的步骤有 3 步
处理accept事件 , 与client 建立连接 , 生成 NioSocketChannel将NioSocketChannel注册到某个worker NIOEventLoop上的selector处理任务队列的任务 即runAllTasks
6) 每个worker NIOEventLoop线程循环执行的步骤
轮询注册到自己selector上的所有NioSocketChannel 的read, write事件这些事件都被注册到Selector上的rdList集合中。事件发生后操作系统会使用中断处理这些事件加入到rdList集合中。处理 I/O 事件 即read , write 事件 在对应NioSocketChannel 处理业务runAllTasks处理任务队列TaskQueue的任务 一些耗时的业务处理一般可以放入TaskQueue中慢慢处理这样不影响数据在 pipeline 中的流动处理
7) 每个worker NIOEventLoop处理NioSocketChannel业务时会使用 pipeline (管道)管道中维护了很多 handler 处理器用来处理 channel 中的数据
Netty模块组件
【Bootstrap、ServerBootstrap】
Bootstrap 意思是引导一个 Netty 应用通常由一个 Bootstrap 开始主要作用是配置整个 Netty 程序串联各个组件Netty 中 Bootstrap 类是客户端程序的启动引导类ServerBootstrap 是服务端启动引导类。
【Future、ChannelFuture】
正如前面介绍在 Netty 中所有的 IO 操作都是异步的不能立刻得知消息是否被正确处理。
但是可以过一会等它执行完成或者直接注册一个监听具体的实现就是通过 Future 和 ChannelFutures他们可以注册一个监听当操作执行成功或失败时监听会自动触发注册的监听事件。
【Channel】
Netty 网络通信的组件能够用于执行网络 I/O 操作。Channel 为用户提供
1当前网络连接的通道的状态例如是否打开是否已连接
2网络连接的配置参数 例如接收缓冲区大小
3提供异步的网络 I/O 操作(如建立连接读写绑定端口)异步调用意味着任何 I/O 调用都将立即返回并且不保证在调用结束时所请求的 I/O 操作已完成。
4调用立即返回一个 ChannelFuture 实例通过注册监听器到 ChannelFuture 上可以 I/O 操作成功、失败或取消时回调通知调用方。
5支持关联 I/O 操作与对应的处理程序。
不同协议、不同的阻塞类型的连接都有不同的 Channel 类型与之对应。
下面是一些常用的 Channel 类型
NioSocketChannel异步的客户端 TCP Socket 连接。
NioServerSocketChannel异步的服务器端 TCP Socket 连接。
NioDatagramChannel异步的 UDP 连接。
NioSctpChannel异步的客户端 Sctp 连接。
NioSctpServerChannel异步的 Sctp 服务器端连接。
这些通道涵盖了 UDP 和 TCP 网络 IO 以及文件 IO。
【Selector】
Netty是基于Selector对象实现的IO多路复用通过Selector一个线程就可以监听到多个连接的Channel事件。当向一个Selector中注册Channel后Selector内部的机制就可以不断的查询这些注册的Channel是否有已经就绪的IO事件(例如可读可写网络连接完成等)这样程序就可以很简单的使用一个线程高效的管理多个Channel
【NioEventLoop】
NioEventLoop中维护了一个线程和任务队列支持异步提交执行任务线程启动时会调用NioEventLoop的run方法执行 I/O 任务和非 I/O 任务
I/O 任务即 selectionKey 中 ready 的事件如 accept、connect、read、write 等由 processSelectedKeys 方法触发。
非 IO 任务添加到 taskQueue 中的任务如 register0、bind0 等任务由 runAllTasks 方法触发。
【NioEventLoopGroup】
NioEventLoopGroup主要管理 eventLoop 的生命周期可以理解为一个线程池内部维护了一组线程每个线程(NioEventLoop)负责处理多个 Channel 上的事件而一个 Channel 只对应于一个线程。
【ChannelHandler】
ChannelHandler是一个接口处理IO事件或者拦截IO操作并且将转发到其ChannelPipeline(业务处理链)中下一个处理程序。
ChannelHandler 本身并没有提供很多方法因为这个接口有许多的方法需要实现方便使用期间可以继承它的子类
ChannelInboundHandler 用于处理入站 I/O 事件。read事件ChannelOutboundHandler 用于处理出站 I/O 操作。write事件
或者使用以下适配器类
ChannelInboundHandlerAdapter 用于处理入站 I/O 事件。 read事件ChannelOutboundHandlerAdapter 用于处理出站 I/O 操作。write事件
【ChannelHandlerContext】
保存 Channel 相关的所有上下文信息同时关联一个 ChannelHandler 对象。
【ChannelPipline】
保存了ChannelHandler的List集合用于处理或拦截Channel的入站事件(read)和出站事件(write)操作。
ChannelPipeline 实现了一种高级形式的拦截过滤器模式使用户可以完全控制事件的处理方式以及 Channel 中各个的 ChannelHandler 如何相互交互。
在 Netty 中每个 Channel 都有且仅有一个 ChannelPipeline 与之对应它们的组成关系如下 一个Channel包含了一个ChannelPipeline而ChannelPipeline又维护了一个由ChannelHandlerContext组成的双向链表并且每一个ChannelHandlerContext中又关联着一个ChannelHandler
read事件(入站事件)和write事件(出站事件)在一个双向链表中入站事件会从链表head往后传递到最后一个入站的handler出站事件会从链表tail往前传递到最前一个出站的handler两种类型的handler互不干扰。