Netty原理解析

1、不得不说的NIO框架

解析传统IO问题,一般使用多线程来解决,一个读写阻塞不会影响其它的线程,但这也有问题,线程上下文切换是有开销的,另外设置过多的线程,也是一种开销。传统IO的根本原因是IO是阻塞,所以一种新的IO模型就产生了NIO(Non-Blocking IO),NIO三把斧(select、channel、buffer)刚好解决了传统IO的问题,具体分析可以详见 漫谈NIO。支持NIO要在操作系统内核支持,应用层调用系统函数进行处理操作,可以使用JDK中的类,但JDK存在一些问题,所以就诞生了NIO框架,但它们的原理都是相似的,只是在细节处理上不同。现在比较流行的NIO框架有Netty、Mina,两个框架都是出于同一个作者。

2、NIO示例

        public class NIOServerDemo {
    private static final int BUF_SIZE = 100;
    private static final int TIME_OUT = 10000;

    public static void main(String args[]) throws Exception {
        // 打开服务端 Socket
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        // 打开 Selector
        Selector selector = Selector.open();

        // 服务端 Socket 监听8080端口, 并配置为非阻塞模式
        serverSocketChannel.socket().bind(new InetSocketAddress(8081));
        serverSocketChannel.configureBlocking(false);
        // 注册OP_ACCEPT事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // 通过调用 select 方法, 阻塞地等待 channel I/O 可操作
            if (selector.select(TIME_OUT) == 0) {
                continue;
            }

            Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                // 先移去
                keyIterator.remove();
                // 可连接
                if (key.isAcceptable()) {
                    System.out.println("one connect is coming");

                    SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
                    clientChannel.configureBlocking(false);
                    // 注册读事件
                    clientChannel.register(key.selector(), OP_READ, ByteBuffer.allocate(BUF_SIZE));
                }
                // 可读
                if (key.isReadable()) {
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    ByteBuffer buf = (ByteBuffer) key.attachment();
                    long bytesRead = clientChannel.read(buf);
                    if (bytesRead == -1) {
                        clientChannel.close();
                    } else if (bytesRead > 0) {
                        key.interestOps(OP_READ | SelectionKey.OP_WRITE);
                        System.out.println("Get data ===>: " + new String(buf.array()));
                    }
                }

            }
        }
    }
}
      

说明:

  • ServerSocketChannel 注册请求(SelectionKey.OP_ACCEPT)事件,并设置监听端口,设置非阻塞请求
  • selector.select() 阻塞式的监听事件,如果有事件返回,它就会有返回,事件类型在selector.selectedKeys()
  • SelectionKey 可以获取SocketChannel,可以把它当作一个客户端连接

上面一个例子"麻雀虽小,但五脏俱全",它基本上把NIO的原理讲清楚了。

3、网络的本质

网络的本质是"请求-处理-响应(返回)",我们平时写的网络程序,基本上都遵循这个规律,一般的步骤是:

  • 1) 服务器监听请求,发现有请求来了,转到具体的处理类上;
  • 2)处理请求也有一系列的步骤:
    • a. 解析请求,转成对应的对象
    • b. 具体业务处理


  • 3)按约定格式返回处理结果

不管使用最基础的Servlet,还是SpringMVC,或者SpringCloud,基本都是上面的步骤。

4、Netty原理

Netty是一个NIO框架,在Netty里,有一种线程模型Reactor,用下面的图来说明。

v2-33fc7a118867a9d0ac70136958e3bdf6_b.jpg


说明:

  • 体现了单一职现原理,将一个网络请求分为:请求+处理,每个专注的事情不一样
  • 左边的boss线程池(一个端口只对应一个线程,所以一般把它设置一个线程),右边是work线程池,每个线程都有一个select,监听channel注册的读写事件
  • boss线程等待连接事件到来,如果到了,就从work线程池中取一个线程出来,让它来等待读写事件到来
  • 事件到来是通过selector.select()来判断的,依赖于操作系统底层支持,所说的异步就是通过这种事件

再回过头来看上面NIO 示例的时候,是不是很清晰了。Netty封装了这两个线程池,里面稍微有一些复杂,让一些初学者感觉到害怕,一步一步的深入的时候,发现原理就是那样的。

源码说明:

  • EventLoopGroup 是池线程池,真正的线程是NioEventLoop
  • 每个线程都有一个selector,它会监听注册的channel事件发生
  • bossGroup设置成单线程,一个端口只会对应一个线程,多个线程监听一个端口会报错的,端口占用,workerGroup可以是多线程,线程数默认是核数*2,假设是4核8线程,每个线程处理1000个请求,共计可处理8000个请求
  • 每个channel有一个pipeline,里面可以加一些handler,如编码、解码、业务处理等,是典型的责任链模式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值