Netty概述
Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持。
- 高并发 :基于NIO,较BIO效率大大提升。
- 传输快 :依赖NIO的零拷贝特性。
- 封装好 : Netty对NIO大部分操作进行了封装,提供易于使用的API,使开发者能能够快速高效的构建一个稳健的高并发应用。
Netty的Reactor线程模型
主从多线程的Reactor模型
mainReacotor,subReactor,Thread Pool均为线程池。
mainReactor负责处理client连接请求,mainReactor负责处理客户端的连接请求,并将accept的连接注册到subReactor的其中一个线程上。(acceptor仅仅完成登录、握手、安全认证等操作)
subReactor负责处理客户端通道上的数据读写
Thread Pool是具体的业务逻辑线程池,处理具体业务。
Netty具体线程模型
-
ServerBootStrap:服务端启动辅助类,负责初始化、注册、绑定等工作。
public class serverDemo{ //线程数默认为CPU核心数*2 EventLoopGroup bossgroup = new NioEventLoopGroup(1); EventLoopGroup workgroup = new NioEventLoopGroup(3); try{ ServerBootstrap b = new ServerBootstrap(); b.group(bossgroup,workgroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG,100) //设置业务职责链 //初始化时生效 .handler(new LoggingHandler(LogLevel.INFO)) //发生连接时生效 .childHandler(new HttpServerInitializer()); //绑定端口 ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); }finally { bossgroup.shutdownGracefully(); workgroup.shutdownGracefully(); }
}
public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
protected void initChannel(SocketChannel sc) throws Exception {
ChannelPipeline pipeline = sc.pipeline();
//处理http消息的编解码
pipeline.addLast("httpServerCodec", new HttpServerCodec());
//添加自定义的ChannelHandler
pipeline.addLast("httpServerHandler", new HttpServerHandler());
}
}
编写责任链:
继承 ChannelInitializer 并实现其中的 initChannel 方法。通过 initChannel 方法参数 sc 得到 ChannelPipeline 的一个实例。当一个新的连接被接受时, 一个新的 Channel 将被创建,同时它会被自动地分配到它专属的 ChannelPipeline。
- Channel:可以把 Channel 看作是传入(入站)或者传出(出站)数据的载体。
bind() 方法调用时,将会创建一个 ServerChannel
当连接被接受时,ServerChannel 将会创建一个新的子 Channel
ServerChannel 和子 Channel 之间是一对多的关系
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException(“channelClass”);
}
// 创建 channelFactory
return channelFactory(new ReflectiveChannelFactory(channelClass));
}
…
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return (B) this;
}
调用channel()接口设置 AbstractBootstrap 的成员变量 channelFactory
// Channel工厂类
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Class<? extends T> clazz;
public ReflectiveChannelFactory(Class<? extends T> clazz) {
if (clazz == null) {
throw new NullPointerException("clazz");
}
this.clazz = clazz;
}
@Override
public T newChannel() {
try {
// 通过反射来进行常见Channel实例
return clazz.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
@Override
public String toString() {
return StringUtil.simpleClassName(clazz) + ".class";
}
}
ReflectiveChannelFactory通过反射获取channel实例。
channel的四种状态:
ChannelUnregistered:Channel已经被创建,但还未注册到EventLoop
ChannelRegistered:Channel已经被注册到了EventLoop
ChannelActive:Channel处于活动状态(已经连接到它的远程节点)。它现在可以接收和发送数据了
ChannelInactive:Channel没有连接到远程节点
-
NioEventLoop:实际上就是工作线程,可以直接理解为一个线程。
每个NioEventLoop都绑定了一个Selector,(Netty中,是由Selector监听IO就绪时间,Channel注册到Selector).
一个Channel绑定到一个NioEventLoop的Selector,一个Selector可以被多个Channel注册。主要职责:
I/O任务和非I/O任务:
I/O 任务就是处理 Nio 中 Selector 中注册的 4 种事件。
SelectionKey.OP_READ :负责读
SelectionKey.OP_WRITE :负责写
SelectionKey.OP_CONNECT : 负责注册监听连接操作位,用于判断异步连接结果
SelectionKey.OP_ACCEPT : 负责处理客户端的请求接入非IO任务
系统 Task:通过调用 NioEventLoop 的 excute(Runnable task) 方法实现, Netty 有很多系统 Task,创建它们的主要
原因:当 I/O 线程和用户线程同时操作网络资源时,为了防止并发操作导致的锁竞争,将用户线程操作封装成 Task 放
入消息队列中,由 NioEventLoop 线程执行,由同一个线程执行,不需要考虑多线程并发问题。
定时任务:通过调用 NioEventLoop 的 schedule(Runnable command,long delay,TimeUnit unit) 方法实现。public final class NioEventLoop extends SingleThreadEventLoop {
private Selector selector; private Selector unwrappedSelector; private SelectedSelectionKeySet selectedKeys; private final SelectorProvider provider; ......
}
NioEventLoop 中可以看到使用了NIO的Selector -
NioEventLoopGroup: NioEventLoop构成的组。
-
ChannelHandler:充当了所有处理入站和出站数据的应用程序逻辑的容器。ChannelHandler 主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等。
ChannelHandler两个重要子接口:
ChannelInboundHandler——处理入站数据以及各种状态变化;
ChannelOutboundHandler——处理出站数据并且允许拦截所有的操作。
ChannelInboundHandler接口
以下方法将会在数据被接收时或者 与其对应的Channel状态发生改变时被调用
public interface ChannelInboundHandler extends ChannelHandler {
/**
* 当Channel已经注册到它的EventLoop并且能够处理I/O时被调用
*/
void channelRegistered(ChannelHandlerContext ctx) throws Exception;
/**
* 当Channel从它的EventLoop注销并且无法处理任何I/O时被调用
*/
void channelUnregistered(ChannelHandlerContext ctx) throws Exception;
/**
* 当Channel处于活动状态时被调用;Channel已经连接/绑定并且已经就绪
*/
void channelActive(ChannelHandlerContext ctx) throws Exception;
/**
* 当Channel离开活动状态并且不再连接它的远程节点时被调用
*/
void channelInactive(ChannelHandlerContext ctx) throws Exception;
/**
* 当从Channel读取数据时被调用
*/
void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;
/**
* 当Channel上的一个读操作完成时被调用
*/
void channelReadComplete(ChannelHandlerContext ctx) throws Exception;
/**
* 当ChannelnboundHandler.fireUserEventTriggered()方法被调用时被调用,因为一个POJO被传经了ChannelPipeline
*/
void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;
/**
* 当Channel的可写状态发生改变时被调用。用户可以确保写操作不会完成得太快(以避免发生OutOfMemoryError)或者可以在Channel变为再次可写时恢复写入。可以通过调用Channel的isWritable()方法来检测Channel的可写性。与可写性相关的阈值可以通过Channel.config().setWriteHighWaterMark()和Channel.config().setWriteLowWater-Mark()方法来设置
*/
void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;
/**
* 如果抛出一个可抛出的异常对象,则调用。
*/
@Override
@SuppressWarnings("deprecation")
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}
ChannelOutboundHandler接口
出站操作和数据将由ChannelOutboundHandler处理。它的方法将被Channel、ChannelPipeline以及ChannelHandlerContext调用
public interface ChannelOutboundHandler extends ChannelHandler {
/**
* 当请求将Channel绑定到本地地址时被调用
*/
void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;
/**
* 当请求将Channel连接到远程节点时被调用
*/
void connect(
ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, ChannelPromise promise) throws Exception;
/**
* 当请求将Channel从远程节点断开时被调用
*/
void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
/**
* 当请求关闭Channel时被调用
*/
void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
/**
* 当请求将Channel从它的EventLoop注销时被调用
*/
void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
/**
* 当请求从Channel读取更多的数据时被调用
*/
void read(ChannelHandlerContext ctx) throws Exception;
/**
* 当请求通过Channel将数据写到远程节点时被调用
*/
void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;
/**
* 当请求通过Channel将入队数据冲刷到远程节点时被调用
*/
void flush(ChannelHandlerContext ctx) throws Exception;
}
- ChannelPipeline:ChannelPipeline 为 ChannelHandler 链提供了一个容器并定义了用于沿着链传播入站和出站事件流的 API。保证ChannelHandler之间的处理顺序。
ChannelHandlerContext:
ChannelHandlerContext代表了ChannelHandler与ChannelPipeline之间的关联,当ChannelHandler被添加至ChannelPipeline中时其被创建,ChannelHandlerContext的主要功能是管理相关ChannelHandler与同一ChannelPipeline中的其他ChannelHandler的交互。
一个事件要么被ChannelInboundHander处理,要么被ChannelOutboundHandler处理,随后,它将通过调用ChannelHandlerContext的实现来将事件转发至同一超类型的下一个处理器
一个Channel包含了一个ChannelPipeline,而ChannelPipeline中又维护了一个由ChannelHandlerContext组成的双向链表。这个链表的头是HeadContext,链表的尾是TailContext,并且每个ChannelHandlerContext中又关联着一个ChannelHandler。
参考及转载文章地址:
https://blog.csdn.net/u013857458/article/details/82527722
https://blog.csdn.net/cj2580/article/details/78124780
https://blog.csdn.net/liushu427/article/details/79482080
https://blog.csdn.net/chenssy/article/details/78703551
https://blog.csdn.net/tarenaww/article/details/83620145
https://www.jianshu.com/p/9e5e45a23309
https://blog.csdn.net/qq_37598011/article/details/83957860