netty基础02_netty组件
netty主要包含下列组件:
Bootstrap 和 ServerBootstrap ->引导类,用来配置netty组件;
Channel ->socket
EventLoop -> 处理io事件的线程
ChannelPipeline ->保存处理过程需要用到的ChannelHandler和ChannelHandlerContext
ChannelHandler ->处理请求
ChannelFuture -> 监听异步操作是否成功
1.Bootstrap
引导类,用来做配置,比如配置如何处理消息;
包括两种:
Bootstrap -》作用于客户端;
ServerBootstrap -》作用于服务端;有两个EventLoopGroup,一个用来获取channel,另一个用来处理channel;
2.Channel
通道是对java原生网络编程api的封装,其顶级接口是Channel;
以TCP编程为例 ,在java中,有两种方式:
1】基于BIO,JDK1.4之前,我们通常使用java.net包中的ServerSocket和Socket来代表服务端和客户端。
2】基于NIO,Jdk1.4引入nio编程之后,我们使用java.nio.channels包中的ServerSocketChannel和SocketChannel来代表服务端与客户端。
在Netty中,对java中的BIO、NIO编程api都进行了封装,分别:
1】使用了OioServerSocketChannel,OioSocketChannel对java.net包中的ServerSocket与Socket进行了封装
2】使用NioServerSocketChannel和NioSocketChannel对java.nio.channels包中的ServerSocketChannel和SocketChannel进行了封装。
3. EventLoop
EventLoop的主要作用是处理channel的IO操作。
一个 EventLoopGroup 包含一个或者多个 EventLoop;
一个 EventLoop 在它的生命周期内只和一个 Thread 绑定;
所有由 EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理;
一个 Channel 在它的生命周期内只注册于一个 EventLoop;
一个 EventLoop 可能会被分配给一个或多个 Channel;
线程安全:
一个Channel只会绑定一个EventLoop,并且一个EvnetLoop只会绑定一个线程;
所以不需要考虑线程线程安全的问题;
因为一个Channel的生命周期中,其所有的io操作都是由同一个线程来执行的;
4. ChannelFuture
Netty 中所有的 I/O 操作都是异步的;
因此一个操作可能不会立即返回;
所以我们需要一种用于在之后的某个时间点确定其结果的方法;
为此, Netty 提供了ChannelFuture 接口;
可以通过ChannelFuture的addListener()方法注册一个监听器ChannelFutureListener以便在某个操作完成时获得通知;
ChannelFuture future = bootstrap.bind(new InetSocketAddress(8080)); future.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { if (channelFuture.isSuccess()) { System.out.println("Server bound"); } else { System.err.println("Bind attempt failed"); channelFuture.cause().printStackTrace(); } } } );
5.ChannelHandler、ChannelPipeline
1)管道和处理器
ChannelHandler用来处理消息;
ChannelHandler接口有两个子接口:
ChannelInboundHandler ->处理进站的消息
ChannelOutboundHandler ->处理出站的消息
多个ChannelHandler会在根据在引导类Bootstrap中的配置,按顺序保存在管道ChannelPipeline中;
对于进站的消息,会按从管道头部到尾部的顺序,被管道中每个ChannelInboundHandler处理;直到到达管道尾部为止;
对于出站数据(即调用write方法写出数据),按从管道尾部开始到管道头部的顺序,依次被每个ChannelOutboundHandler处理,直到管道头部时,封装成socket写出;
多个ChannelHandler构成handler链;
事件可以通过ChanneHandlerContext传递给下一个handler;
2)netty提供的处理器
pipeline中每个 ChannelHandler负责处理事件并转发到下一个handler;
因此在程序中需要编写事件处理器并配置到管道中;
为了简化开发,netty提供了一些默认的处理器适配器(也就是抽象类,实现了ChannelHandler接口的一些方法);
常用适配器:ChannelHandlerAdapter、ChannelInboundHandlerAdapter、ChannelOutboundHandlerAdapter、ChannelDuplexHandlerAdapter
常用的适配器子类:
1】编码器、解码器
解码:收到消息时,需要将二进制字节转换成java对象;
编码:响应时,将java对象转成二进制字节;
常用的编码器/解码器: ByteToMessageDecoder、MessageToByteEncoder
所有的编码器/解码器适配器类 都实现自 ChannelInboundHandler或ChannelOutboundHandler。
2】 SimpleChannelInboundHandler
继承自ChannelInboundHandlerAdapter;
用来处理进站消息;
需要实现抽象方法channelRead0(ChannelHandlerContext,T)来对消息进行处理;
T为泛型,也就是解码后消息的类型;
例如:处理字符串
ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) { System.out.println(msg); } });
3)ChannelHandlerContext
当 ChannelHandler 被添加到 ChannelPipeline 时,它将会被分配一个 ChannelHandlerContext;
其代表了 ChannelHandler 和 ChannelPipeline 之间的绑定;
ChannelHandlerContext的作用:
可以获取Channel ->channel()方法;
可以获取管道 ->pipeline()方法;
可以写出数据 ->write()/writeAndFlush()方法;
4)关于write方法
Channel、ChannelPipeline和ChannelHandlerContext都有write方法, 用来写出数据;
Channel -> Channel 上的 write()方法将会导致写入事件从尾端到头部地流经ChannelPipeline,并被每个ChannelOutboundHandler处理;
ChannelPipeline ->和Channel的write()方法一样,写出的出站消息都是从管道尾部开始向头部流动;
ChannelHandlerContext ->ChannelHandlerContext的write()方法,写出事件会从与ChannelHandlerContext绑定的前一个出站处理器开始向管道头部流动;
例如:有4个ChannelHandler
in1通过Channel写出,ctx.channel().write(xxx):
则写出事件执行顺序为:out2 ->out1 ->管道头部
in1通过ChannelHandlerContext写出(ctx.write(xxx)):
写出事件直接从in1的前一个处理器处理,这里in1前面没有出站处理器了,因此直接从管道头部出站;
in2通过Channel写出:out2 ->out1 ->管道头部;
in2通过ChannelHandlerContext写出:out1 ->管道头部;