Netty入门到超神系列-Netty入门&核心类

简介: 我们需要有GoosGroup来循环监听请求事件,需要有WorkGroup来处理事件,而这两个角色都通过来就NioEventLoopGroup来进行事件监听,我们还需要创建事件处理器ChannelHandler,通过 Channel的ChannelPipeline把ChannelHandler进行关联。

前言

上一章讨论的是Netty的线程模型,这一章我们基于上一章的线程模型来实战一把。由于Netty5出现重大BUG被官方废弃,所以我们使用Netty4进行学习。

Netty入门实战

在开始之前再来看一下Netty的线程模型

我们需要有GoosGroup来循环监听请求事件,需要有WorkGroup来处理事件,而这两个角色都通过来就NioEventLoopGroup来进行事件监听,我们还需要创建事件处理器ChannelHandler,通过 Channel的ChannelPipeline把ChannelHandler进行关联。

导入依赖

我们来写一个简单的服务端和客户端通信的案例,首先创建Maven项目,导入Maven依赖

<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.42.Final</version></dependency>

服务端代码

对于服务端我们要做如下事情

  1. 创建两个NioEventLoopGroup ,一个作为BossGroup ,一个作为 WorkGroup
  2. 创建 ServerBootstrap 加入两个NioEventLoopGroup
  3. 通过 ServerBootstrap 添加Handler ,需要自己创建Handler
  4. 编写Handler ,通过继承ChannelInboundHandlerAdapter 来对不同的事件做不同的处理

服务端代码如下

publicclassNettyServer {
publicstaticvoidmain(String[] args) throwsInterruptedException {
//创建Boss Group :负责处理链接NioEventLoopGroupbossGroup=newNioEventLoopGroup();
//创建 workGroup :负责处理读写事件NioEventLoopGroupworkGroup=newNioEventLoopGroup();
//创建服务端启动对象ServerBootstrapbootstrap=newServerBootstrap();
try{
//把 bossGroup 和 workGroup 加入启动对象bootstrap                    .group(bossGroup,workGroup)
//保存活动链接状态,启用该功能时,TCP会主动探测空闲连接的有效性                    .childOption(ChannelOption.SO_KEEPALIVE,true)
//多个客户端过来,处理不过来的请求在队列排队,指定存放请求的队列的大小                    .option(ChannelOption.SO_BACKLOG,64)
//指定服务器使用什么样的通道                    .channel(NioServerSocketChannel.class)
//添加事件处理器Handler                    .childHandler(newChannelInitializer<SocketChannel>(){
@OverrideprotectedvoidinitChannel(SocketChannelch) throwsException {
//把处理器添加进去 , 每个channel都对应一个pieline//pieline通道和Handler进行绑定,通过Pieline可以获取到Handlerch.pipeline().addLast(newMyServerHandler());
                        }
                    });
//绑定端口,开始接收客户端请求//bind方法中会创建 Channel,然后使用 EventLoop注册Channel ChannelFuturefuture=bootstrap.bind(newInetSocketAddress("127.0.0.1", 6000)).sync();
//对关闭通道进行监听//当此通道关闭时将收到通知future.channel().closeFuture().sync();
        }catch (Exceptione){
e.printStackTrace();
        }finally {
//关闭资源workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
        }
    }
}
  • ChannelOption.SO_BACKLOG:服务端处理客户端连接请求是顺序处理的,所以同一事件只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小
  • ChannelOption.SO_KEEPALIVE:当设置该选项以后,如果在两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文。

handler代码如下

//这个Handler是用来处理客户端channel的IO事件publicclassMyServerHandlerextendsChannelInboundHandlerAdapter {
//处理读事件 ChannelHandlerContext :上下文,含有通道,pipline ,地址 ;  msg就是数据@OverridepublicvoidchannelRead(ChannelHandlerContextctx, Objectmsg) throwsException {
ByteBufbuffer= (ByteBuf) msg;
System.out.println("MyServerHandler,接收到:"+ctx.channel().remoteAddress()+"发来的消息:"+buffer.toString(CharsetUtil.UTF_8));
    }
//数据读取完毕@OverridepublicvoidchannelReadComplete(ChannelHandlerContextctx) throwsException {
//给客户端恢复消息ctx.writeAndFlush(Unpooled.copiedBuffer("你好,我收到了消息",CharsetUtil.UTF_8));
    }
}

上面有几个陌生的类

  • ChannelInboundHandlerAdapter :这个是为Channel通道绑定处理事件的Handler,用来接收请求。
  • ByteBuf :Netty提供的byte缓冲区
  • Unpooled.copiedBuffer :工具,把数据拷贝到ByteBuf

客户端代码

客户端要做如下事情

  1. 创建 NioEventLoopGroup 用于监听服务端的事件
  2. 创建 Bootstrap启动对象 ,加入 NioEventLoopGroup
  3. 一样要通过 Bootstrap 添加Handler ,需要自己创建Handler
  4. 编写Handler ,通过继承ChannelInboundHandlerAdapter 来对不同的事件做不同的处理

客户端代码如下

publicclassNettyClient {
publicstaticvoidmain(String[] args) throwsInterruptedException {
//客户端线程循环组NioEventLoopGroupeventExecutors=newNioEventLoopGroup();
//客户端启动对象Bootstrapbootstrap=newBootstrap();
try {
bootstrap.group(eventExecutors)
//指定客户端使用的通道类型                    .channel(NioSocketChannel.class)
//添加客户端处理器                    .handler(newChannelInitializer<SocketChannel>() {
@OverrideprotectedvoidinitChannel(SocketChannelch) throwsException {
//添加客户端处理器ch.pipeline().addLast(newMyClientHandler());
                        }
                    });
//链接服务端ChannelFuturechannelFuture=bootstrap                    .connect(newInetSocketAddress("127.0.0.1", 6000)).sync();
//监听关闭事件channelFuture.channel().closeFuture().sync();
        } catch (InterruptedExceptione) {
e.printStackTrace();
        } finally {
eventExecutors.shutdownGracefully();
        }
    }
}

客户端handler代码如下

//这个Handler是用来处理客户端channel的IO事件publicclassMyClientHandlerextendsChannelInboundHandlerAdapter {
//链接激活@OverridepublicvoidchannelActive(ChannelHandlerContextctx) throwsException {
//给服务端发送消息ctx.writeAndFlush(Unpooled.copiedBuffer("你好服务端",CharsetUtil.UTF_8));
    }
//处理读事件 ChannelHandlerContext :上下文,含有通道,pipline ,地址 ;  msg就是数据@OverridepublicvoidchannelRead(ChannelHandlerContextctx, Objectmsg) throwsException {
ByteBufbuffer= (ByteBuf) msg;
System.out.println("MyClientHandler,接收到:"+ctx.channel().remoteAddress()+"发来的消息:"+buffer.toString(CharsetUtil.UTF_8));
    }
//处理异常@OverridepublicvoidexceptionCaught(ChannelHandlerContextctx, Throwablecause) throwsException {
cause.printStackTrace();
ctx.close();
    }
}

分别启动服务端和客户端,最终效果如下

  • 服务端
  • 客户端

核心类认识

在上面的入门案例中涉及到一些类,这里做一个解释

NioEventLoopGroup

NioEventLoopGroup 继承于 EventLoopGroup ,代表Nio 事件循环组,用来循环监听注册的Channel的IO事件,如上图,我们需要两个NioEventLoopGroup,一个为BossGroup做事件监听 ,一个为 WorkGroup做事件监听

ServerBootstrap,Bootstrap

ServerBootstrap是Netty服务端启动引导对象 ,Bootstrap是客户端的启动引导对象,作用都差不多,主要是用来配置整个netty程序,整合各个组件。

ServerBootstrap 常用方法如下:

  • group(EventLoopGroup parentGroup, EventLoopGroup childGroup) : 该方法用于服务端,主要是为 BossGroup(Acceptor) 设置 EventLoopGroup 来监听连接事件,以及为 WorkGroup 设置EventLoopGroup来监听读写事件。
  • channel(Class<? extends C> channelClass) : 指定用来创建channel通道实例的class
  • option(ChannelOption option, T value) : 方法的作用是用来给ServerChannel添加配置项
  • childOption(ChannelOption childOption, T value) :为请求的channel添加配置项
  • handler(ChannelHandler handler) : 给BossGrop添加Handler
  • childHandler(ChannelHandler childHandler) : 给WorkGroup添加Handler
  • bind(InetAddress inetHost, int inetPort) : 为服务端绑定监听的地址和端口

Bootstrap 常用方法如下

  • connect(InetAddress inetHost, int inetPort) :用来连接服务端
  • group(EventLoopGroup group) : 为客户端添加EventLoopGroup事件循环组。

Future,ChannelFuture

由于Netty的IO是异步的,不能立即得到消息是否被处理完成,所以需要通过Future,ChannelFuture 注册事件监听,当操作完成后会触发相应的事件,从而对事件结果做出相应的处理。

Channel

通道,用来关联IO操作和处理的Handler,不同的协议有不同的Channel

  • NioServerSocketChannel : 服务端的TCP Socket 连接
  • NioSocketChannel :客户端的TCP Socket连接

ChannelHandler

ChannelHandler是用来处理IO事件的Handler,Handler实际上分为两种,Inbound 入站和Outbound出站,ChannelHandler比较常用的有如下两个子类

  • ChannelInboundHandlerAdapter
    该适配器 实现了ChannelInboundHandler 接口,ChannelInboundHandler 又实现了ChannelHandler接口,主要是用来处理IO入站事件
  • ChannelOutboundHandlerAdapter
    适配器 实现了ChannelOutboundHandler,ChannelOutboundHandler又实现了 ChannelHandler接口,它主要是用处理IO出站事件
  • ChannelDuplexHandler :双通道处理器,它同时实现了ChannelInboundHandler 和 ChannelOutboundHandler ,既可以处理IO入站事件,也可以处理IO出站事件。

ChannelPipeline

对于每个新的通道Channel,都会创建一个新的ChannelPipeline,并将器pipeline附加到channel中,可以把ChannelPipeline看成是一个ChandlerHandler的链表,当有事件发生的时候,Pipeline负责依次调用每一个Handler进行处理或截获通道的接收和发送数据,ChannelPipeline 通过 ChannelHandlerContext来管理ChannelHandler

下面是端点后的 ChannelPipeline的调用链截图

通过Cahnnel可以获取到ChannelPipeline,通过ChannelPipeline也可以获取到Channel,ChannelPipeline是ChandlerHandler形成的双向链表 ,ChandlerHandler中包含了真正的Handler , 上图的Head是头节点,tail是尾节点 。 如下图:

所以整个IO事件的处理是通过 Channel ,ChannelPipeline 和 Handler 三者一起来完成的。这里要注意的是,如果事件运动方向是从客户端到服务端,数据会通过pipeline中的一些列的OutboundHandler进行处理,事件被称之为出站,反之就是入站。两种类型的Handller相互不影响

Unpooled

Unpooled是Netty提供的buffer缓冲工具类,可以根据给定的数据和编码创建一个ByteBuf,如 ByteBuf buffer = Unpooled.buffer(1024);。ByteBuf类似于NIO中的ByteBuffer 。区别在于ByteBuffer需要调用flip()读写转换,而ByteBuf无需转换。

和ByteBuffer一样,ByteBuf中维护了一个byte[]来存储数据,同时还维护了一个writerIndex和readerIndex,当执行 buffer.writeByte 写操作writerIndex就会向后移动,指向下一个存储位置。当执行 buffer.readByte()操作的时候readerIndex就会向后移动指向下一个读的位置。当readerIndex等于writerIndex,ByteBuf中的数据读取完成。

目录
相关文章
|
6月前
|
缓存 网络协议 算法
Netty的基础入门(上)
Netty的基础入门(上)
223 1
|
6月前
|
缓存 网络协议 算法
《跟闪电侠学Netty》阅读笔记 - Netty入门程序解析
《跟闪电侠学Netty》阅读笔记 - Netty入门程序解析
209 0
|
7天前
|
消息中间件 编解码 网络协议
Netty从入门到精通:高性能网络编程的进阶之路
【11月更文挑战第17天】Netty是一个基于Java NIO(Non-blocking I/O)的高性能、异步事件驱动的网络应用框架。使用Netty,开发者可以快速、高效地开发可扩展的网络服务器和客户端程序。本文将带您从Netty的背景、业务场景、功能点、解决问题的关键、底层原理实现,到编写一个详细的Java示例,全面了解Netty,帮助您从入门到精通。
26 0
|
6月前
|
存储 消息中间件 缓存
Netty的基础入门(下)
Netty的基础入门(下)
119 0
|
6月前
|
前端开发 网络协议 Java
Netty入门指南:从零开始的异步网络通信
Netty入门指南:从零开始的异步网络通信
180 0
|
6月前
|
设计模式 网络协议 算法
《跟闪电侠学Netty》阅读笔记 - Netty入门程序解析(一)
《跟闪电侠学Netty》阅读笔记 - Netty入门程序解析(一)
221 1
《跟闪电侠学Netty》阅读笔记 - Netty入门程序解析(一)
|
6月前
|
消息中间件 缓存 Java
《跟闪电侠学Netty》阅读笔记 - 开篇入门Netty(二)
《跟闪电侠学Netty》阅读笔记 - 开篇入门Netty
244 1
《跟闪电侠学Netty》阅读笔记 - 开篇入门Netty(二)
|
6月前
|
消息中间件 缓存 Java
《跟闪电侠学Netty》阅读笔记 - 开篇入门Netty
《跟闪电侠学Netty》阅读笔记 - 开篇入门Netty
171 0
|
6月前
|
缓存 网络协议 算法
《跟闪电侠学Netty》阅读笔记 - Netty入门程序解析(二)
《跟闪电侠学Netty》阅读笔记 - Netty入门程序解析
119 1
|
6月前
|
缓存 Java 数据挖掘
《跟闪电侠学Netty》阅读笔记 - 开篇入门Netty(一)
《跟闪电侠学Netty》阅读笔记 - 开篇入门Netty
149 0
《跟闪电侠学Netty》阅读笔记 - 开篇入门Netty(一)