- Netty编程(一)—— 初识Netty+超全注释
- 什么是Netty
- Hello World
- 服务端
- 客户端
- 执行流程
- 组件解释
之前的博客介绍了NIO网络编程的相关知识,从这篇博客开始,我将开始介绍Netty的相关知识。
什么是NettyNetty 是一个异步的、基于事件驱动的网络应用框架,可用于快速开发可维护、高性能的网络服务器和客户端
- 基于事件驱动意思是底层实现采用多路复用技术(selector),事件发生时才需要进行处理
- 异步是指使用了多线程完成方法调用和处理结果相分离,并不是异步IO
学习一个技术或者框架,可以先从hello world开始了解它,然后一步一步进行学习。下面就先通过一段最基础的Netty代码来初始Netty,这段代码可以看作Netty的Hello World,它分为服务器端和客户端两部分,首先来看服务端的代码
服务端public class HelloServer { public static void main(String[] args) { // 1、服务器端的启动器,负责装配下方的netty组件,启动服务器 new ServerBootstrap() // 2、创建 NioEventLoopGroup,可以简单理解为 线程池 + Selector .group(new NioEventLoopGroup()) // 3、选择服务器的 ServerSocketChannel 实现 .channel(NioServerSocketChannel.class) // 4、child(work) 负责处理读写,该方法决定了 child(work) 执行哪些 *** 作(handler) // ChannelInitializer 处理器(仅执行一次) // 5、channel的作用是待客户端SocketChannel建立连接后与客户端进行读写的通道,执行initChannel初始化,作用是添加别的handler .childHandler(new ChannelInitializer() { @Override protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {//添加handler // 6、添加具体的handler nioSocketChannel.pipeline().addLast(new StringDecoder());//使用StringDecoder解码,ByteBuf=>String nioSocketChannel.pipeline().addLast(new SimpleChannelInboundHandler () {// 自定义handler,使用上一个处理器的处理结果 @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception { System.out.println(s);//打印上一步转换好的字符串 } }); } // 7、ServerSocketChannel绑定8080端口 }).bind(8080); } }
下面对这段代码进行解读:
- 首先使用new ServerBootstrap()打开一个服务端的启动器,它负责装配下面的Nettty组件,同时会启动服务器
- group(new NioEventLoopGroup())用来创建一个事件循环组EventLoopGroup,可以把它理解成是Selector+线程池的组合
- channel(NioServerSocketChannel.class)选择服务器的ServerSocketChannel 实现
- childHandler(new ChannelInitializer
()中的child可以理解成worker,是负责处理读写事件的,这个方法决定了child执行哪些 *** 作(handler) - channel的作用是待客户端SocketChannel建立连接后与客户端进行读写的通道,执行initChannel初始化,作用是添加别的handler
- initChannel(NioSocketChannel nioSocketChannel)方法是用来添加具体的handler,具体是使用nioSocketChannel.pipeline().addLast来添加,代码中添加了第一个handler是解码,将客户端发来的数据变成String,添加了第二个handler是自定义handler,并且这个handler需要使用上一个解码handler的结果。
- bind(8080)最后使用bind方法对端口号进行绑定
public class HelloClient { public static void main(String[] args) throws InterruptedException { //1、启动类 new Bootstrap() //2、添加EventLoop .group(new NioEventLoopGroup()) // 3、选择客户 Socket 实现类,NioSocketChannel 表示基于 NIO 的客户端实现 .channel(NioSocketChannel.class) // 4、添加处理器 ChannelInitializer 处理器(仅执行一次) // 它的作用是待客户端SocketChannel建立连接后,执行initChannel以便添加更多的处理器 .handler(new ChannelInitializer() {//初始化器会在连接建立后被调用,调用后就会执行下面的initChannel @Override protected void initChannel(NioSocketChannel channel) throws Exception { // 消息会经过通道 handler 处理,这里是将 String => ByteBuf 编码发出 channel.pipeline().addLast(new StringEncoder()); } }) // 指定要连接的服务器和端口 .connect(new InetSocketAddress("localhost", 8080)) // Netty 中很多方法都是异步的,如 connect // 这时需要使用 sync 方法等待 connect 建立连接完毕,是一个阻塞方法,知道连接建立 .sync() // 获取 channel 对象,它即为通道抽象,可以进行数据读写 *** 作 .channel() // 写入消息并清空缓冲区,不管收发数据,都会走handle,调用处理器内部的方法 .writeAndFlush("hello world");//把字符串转成了bytebuf } }
可以看到其实客户端代码与服务端代码类似,下面对这段客户端的代码进行解读:
- new Bootstrap()启动一个客户端
- group(new NioEventLoopGroup())打开一个事件循环组,可以向其中添加handler
- channel(NioSocketChannel.class)选择客户 Socket 实现类,NioSocketChannel 表示基于 NIO 的客户端实现
- handler(new ChannelInitializer
()添加 ChannelInitializer 处理器,它的作用是待客户端SocketChannel 建立连接 后,执行initChannel以便添加更多的处理器 - connect(new InetSocketAddress("localhost", 8080))指定要连接的服务器和端口
- sync()的作用是阻塞,他等待connect连接完毕
- channel()获取 channel 对象,它即为通道抽象,可以进行数据读写 *** 作
- writeAndFlush("hello world")写入消息并清空缓冲区,无论收发数据,都会通过handle,调用处理器内部的方法
有以下几点执行顺序与代码顺序不同:
- 服务器端初始化了ChannelInitializer 处理器后,会执行最后一行的bind方法进行绑定端口号,之后等待客户端的连接
- 客户端初始化ChannelInitializer后会去执行connect方法连接服务端,连接未成功之前会阻塞住,在连接成功后会立即执行上面的initChannel方法来添加handler
- 客户端拿到channel连接对象后发送数据"hello world",然后添加handler会把String转成ByteBuf(类似于ByteBuffer)后发送给服务端
- 服务端发现有读事件发生后,会启用事件循环组EventLoopGroup中的一个EventLoop去处理这个读事件,调用具体的handler进行处理
-
eventLoop 可以理解为处理数据的工人
- eventLoop 可以管理多个 channel 的 io *** 作,并且一旦 eventLoop 负责了某个 channel,就会将其与channel进行绑定(channel使用工人1发送数据了,之后channel要接收数据,那么还是使用工人1进行处理),即以后该 channel 中的 io *** 作都由该 eventLoop 负责,这是为了线程安全,防止消息覆盖了
- eventLoop 既可以执行 io *** 作,也可以进行任务处理,每个 eventLoop 有自己的任务队列,队列里可以堆放多个 channel 的待处理任务
- eventLoop 按照 pipeline 顺序,依次按照 handler 的规划(代码)处理数据,可以为每个 handler 指定不同的 eventLoop
-
handler 可以理解为数据的处理工序
-
工序有多道,合在一起就是 pipeline(传递途径),pipeline 负责发布事件(读、读取完成…)传播给每个 handler, handler 对自己感兴趣的事件进行处理(重写了相应事件处理方法)
-
pipeline 中有多个 handler,处理时会依次调用其中的 handler
-
handler 分 Inbound 和 Outbound 两类
- Inbound 入站,写入
- Outbound 出站,写出
-
-
channel 可以理解为数据的通道
-
msg 理解为流动的数据,最开始输入是 ByteBuf,但经过 pipeline 中的各个 handler 加工,会变成其它类型对象,最后输出又变成 ByteBuf
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)