前言

在目前许多开源的框架中,避免不了服务与服务之间相互通信,相互查询数据等情况,而往往我们知道的grpc等等,底层也都是由Netty去实现,这里就简单的给大家扫下盲。

Netty简单介绍

什么是经典的三种I/O模型

模式名版本说明
BIO(阻塞I/O)JDK1.4之前同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善
NIO(非阻塞I/O)JDK1.4(2002年,java.nio包)同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
AIO(异步I/O)JDK1.7(2011年)异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理

使用场景

  • BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

  • NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

  • AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

Netty对三种I/O模型的支持

BIO->OIONIOAIO
COMMONLinuxMacOs/BSD
ThreadPerChannelEventLoopGroupNioEventLoopGroupEpollEventLoopGroupKQueueEventLoopGroupAioEventLoopGroup
ThreadPerChannelEventLoopNioEventLoopEpollEventLoopKQueueEventLoopAioEventLoop
OioServerSocketChannelNioServerSocketChannelEpollServerSocketChannelKQueueServerSocketChannelAioServerSocketChannel
OioSocketChannelNioSocketChannelEpollSocketChannelKQueueSocketChannelAioSocketChannel

为什么Netty仅支持NIO?

  1. 为什么不建议阻塞I/O(BIO/OIO)?
    1. 连接数高的情况下:阻塞->耗资源、效率低
  2. 为什么删除已经做好的AIO支持?
    1. Window实现成熟,但是很少用作服务器
    2. Linux常用来做服务器,但是AIO实现不够成熟
    3. Linux下AIO相比较NIO的性能提升不明显

为什么Netty有多种NIO实现?

实现的更好:复制代码
  1. Netty暴露了更多的可控参数
    1. JDK的NIO默认实现是水平触发
    2. Netty是边缘触发和水平触发可切换
  2. Netty实现的垃圾回收更少、性能更好

NIO一定优于BIO么?

  1. BIO代码简单
  2. 特定场景:连接数少,并发度低。

源码解读Netty怎么切换I/O模式?

工厂+反射

Netty如何支持三种Reactor

什么是Reactor

  反应器设计模式(Reactor pattern)是一种为处理并发服务请求,并将请求提交到一个或者多个服务处理程序的事件设计模式。当客户端请求抵达后,服务处理程序使用多路分配策略,由一个非阻塞的线程来接收所有的请求,然后派发这些请求至相关的工作线程进行处理。

Reactor有几种模式

  • Reactor单线程
  • Reactor多线程
  • Reactor主从多线程
BIONIOAIO
Thread=Per-ConnectionReactorProactor

注册感兴趣的事件 -->扫描是否有感兴趣的事件发生 -->时间发生后做出相应的处理

client/ServerSocketChannel/ServerSocketChannelOP_ACCEPTOP_CONNECTOP_WRITEOP_READE
clientSocketChannelYYY
serverSewrverSocketChannelY


serverSocketChannel
YY

如何在Netty中使用Reactor模式

模式名称使用方法
Reactor单线程模式EventLoopGroup bossGroup = new NioEventLoopGroup(1);
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup)
非主从Reactor多线程模式EventLoopGroup bossGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup)
主从Reactor多线程模式EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)

Netty对Reactor模式的支持

通过上面的三种简单的介绍,我们知道Netty针对于Reactor模式是如何支持的,我们除了知道是如何支持的之外,还是需要了解其中是如何实现的,这里采用主从Reactor多线程模式简单的说明下。

主要代码部分:

    public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {super.group(parentGroup);if (this.childGroup != null) {throw new IllegalStateException("childGroup set already");
        }this.childGroup = ObjectUtil.checkNotNull(childGroup, "childGroup");return this;
    }复制代码

我们可以从代码启动开始寻找:

ChannelFuture regFuture = config().group().register(channel);if (regFuture.cause() != null) {if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }复制代码

上面即是serverChannel的注册。

        public void channelRead(ChannelHandlerContext ctx, Object msg) {final Channel child = (Channel) msg;

            child.pipeline().addLast(childHandler);

            setChannelOptions(child, childOptions, logger);
            setAttributes(child, childAttrs);try {
                childGroup.register(child).addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }
        }复制代码

上面即是Channel的注册。

通过上面的两步操作即可完成对应的Reactor模式支持。

总结

上文中简单的介绍了我们底层框架中Netty的一些介绍,当然强大的框架远远不止这些东西去支持,还有需要进行处理的(例如粘包、Metric等)。