Netty的模式

发表时间:2017-10-23 10:03:00 浏览量( 29 ) 留言数( 0 )

学习目标:

1、了解Reactor和Proactor的发展

2、了解Reactor的三种模式


学习过程:

一、Reactor和Proactor的介绍

Reactor

Reactor,即反应堆。Reactor 的一般工作过程是首先在 Reactor 中注册(Reactor)感兴趣事件,并在注册时候指定某个已定义的回调函数(callback);当客户端发送请求时,在 Reactor 中会触发刚才注册的事件,并调用对应的处理函数。在这一个处理回调函数中,一般会有数据接收、处理、回复请求等操作。


Proactor

从上面 Reactor 模式中,发现服务端数据的接收和发送都占用了用户状态(还有一种内核态),这样服务器的处理操作就在数据的读写上阻塞花费了时间,节省这些时间的办法是借助操作系统的异步读写;异步读写在调用的时候可以传递回调函数或者回送信号,当异步操作完毕,内核会自动调用回调函数或者发送信号。Proactor 就是这么做的,所以很依赖操作系统。


二、Reactor的三种模式

事实上,Netty的线程模型并非固定不变,在启动辅助类中创建不同的EventLoopGroup实例并通过适当的参数配置,就可以支持上述三种Reactor线程模型。正是因为Netty对Reactor线程模型的支持提供了灵活的定制能力,所以可以满足不同业务场景的性能需

1 单线程模式

   这是最简单的单Reactor单线程模型。Reactor线程是个多面手,负责多路分离套接字,Accept新连接,并分派请求到处理器链中。该模型适用于处理器链中业务处理组件能快速完成的场景。不过这种单线程模型不能充分利用多核资源,所以实际使用的不多。对于一些小容量应用场景,可以使用单线程模型。但是对于高负载、大并发的应用却不合适,主要原因如下:

一个线程同时处理成百上千的链路,性能上无法支撑,即便CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送;

当负载过重后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈;

一旦单线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障,可靠性不高。

attcontent/777f9a17-2bd0-4dd4-96fa-b14d97b0d023.png


示例代码如下:

 final EchoServerHandler serverHandler = new EchoServerHandler();
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                .channel(NioServerSocketChannel.class)
                .localAddress(new InetSocketAddress(port))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(serverHandler);
                    }
                });

            ChannelFuture f = b.bind().sync();
            System.out.println(EchoServerSingle.class.getName() +
                " 启动了,并开始监听: " + f.channel().localAddress());
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }

2、多线程模型:

有专门一个Acceptor线程用于监听服务端,接收客户端的TCP连接请求;在绝大多数场景下,Reactor多线程模型都可以满足性能需求;但是,在极特殊应用场景中,一个NIO线程负责监听和处理所有的客户端连接可能会存在性能问题。例如百万客户端并发连接,或者服务端需要对客户端的握手消息进行安全认证,认证本身非常损耗性能。在这类场景下,单独一个Acceptor线程可能会存在性能不足问题,为了解决性能问题,产生了第三种Reactor线程模型-主从Reactor多线程模型。

attcontent/d2f4d231-8d67-47f0-8a22-ddfe96912c05.png

代码如下:

        EventLoopGroup linsgroup = new NioEventLoopGroup(1); //设置为1
        EventLoopGroup busGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(linsgroup,busGroup)

3、主从多线程模型

   比起第二种模型,它是将Reactor分成两部分,mainReactor负责监听并accept新连接,然后将建立的socket通过多路复用器(Acceptor)分派给subReactor。subReactor负责多路分离已连接的socket,读写网络数据;业务处理功能,其交给worker线程池完成。通常,subReactor个数上可与CPU个数等同。这也是Netty推荐使用的方式

attcontent/171f33e8-70fe-4c86-bd1b-4695e5e9f26b.png


示例代码如下:

        EventLoopGroup linsgroup = new NioEventLoopGroup();//不写默认就是NettyRuntime.availableProcessors() * 2
        EventLoopGroup busGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(linsgroup,busGroup)

从各个源代码可以看到,Netty对这三种模式的支持非常好,而且使用非常简单。