一、Netty介绍1.1简介官网开头有这么一句话介绍NettyNetty是一个异步事件驱动的网络应用框架,用于快速开发可维护性高performanceprotocolservers&clients是指Netty是一个异步的、事件驱动的网络应用框架,用于快速开发高性能协议服务器和客户端。怎么理解这句话?异步是因为Netty中几乎所有的操作都是异步的。所谓事件驱动,如果我们简单理解的话,就好比我们在使用电脑的时候,你点击什么按钮(也就是产生什么事件),电脑执行什么操作(就是调用什么函数)),当然,事件不仅限于用户的操作,在网络中,事件可以是建立连接、读写数据等。1.2Netty的架构Netty的架构图如下,摘自官网核心部分:支持零拷贝的升级版ByteBuffer(不了解零拷贝概念的童鞋可以查资料自己)统一交互API可扩展事件模型传输服务:Socket&Datagram,对应TCP和UDPHTTP隧道JVM内部管道传输协议支持:也有很多协议的支持,包括HTTP、WebSocket、SSL、googleprotobuf等协议1.3Netty的主要特点是基于NIO实现,具有高吞吐量和低延迟(没有学过NIO的童鞋需要先学习NIO)支持多种协议,开箱即用的统一API,适用于各种传输类型可定制线程模型(和后面要说的Reactor模型有关)1.4Netty中的组件这里先介绍一下Netty中的组件,先熟悉一下,再详细介绍ByteBuf:缓冲区,包括堆内存和直接内存ChannelPromise:异步操作相关的EventLoopEventLoopGroup:EventLoop是事件循环的意思,在单个线程中执行各种IO事件,一个EventLoopGroup对应多个EventLoopChannels:每个Channel都会注册到一个EventLoop,一个EventLoop可以对应多个Channel,每个Channel包含一个ChannelPipeline管道ChannelPipeline和ChannelHandler:ChannelPipeline管道是由ChannelHandler构成的双向链表Bootstrap:启动相关在上面Netty的主要特性中,我们可以看到Netty具有可定制线程模型的特性,那么这是什么意思呢?其实Netty可以很方便的在Reactor的几种线程模型之间切换,所以我们需要了解Reactor模式2,Reactor模式2.1什么是Reactor模式?Reactor模式的定义如下:Reactor设计模式是一种事件处理模式,用于处理由一个或多个输入并发传递的服务请求。服务处理程序然后多路分解传入请求并同步分发给关联的requesthandlers大致意思是:Reactor是一种专用于处理事件的模式,用于处理来自一个或多个输入源的并发服务请求,ServiceHandler将传入的服务请求分发给关联所谓Reactor模式基于IO多路复用+非阻塞IO,对应传统的多线程+阻塞IO模式(即每个线程处理一个Connection)IO多路复用:通过操作系统提供的selet/poll/epoll等操作来实现实现单线程管理多个连接,称为IO多路复用非阻塞IO:当调用读、写等IO操作时,当前线程没有被阻塞,称为非阻塞IO。Reactor模式一般有三种实现方式,分别是单Reactor单线程、单Reactor多线程和主从Reactor多线程。接下来我们将2.2三种常见的Reactor一一介绍。Reactor这个模型翻译过来就是反应器,也叫分发器。它的作用是监听多个连接,并将监听到的IO事件分发给相应的处理器进行处理。2.2.1SingleReactorSingleThreadSingleReactor单线程是指只有一个Reactor,注册新的连接和监听连接的IO事件,并在一个线程中处理这些IO事件,如下图所示:Reactor监听客户如果最后的request事件是连接建立事件,将事件分发给acceptor处理,acceptor将新的连接注册到Reactor。如果是读或写事件,将事件分发给对应的handler进行处理。示例代码基于JavaNIO示例代码如下,可以使用telnetlocalhost9999进行测试,服务端会打印出接收到的内容并返回接收到的数量/***说明:SingleReactorsinglethread*Createdbykamier*/publicclassSingleReactorSingleThread{publicstaticvoidmain(String[]args)throwsIOException{//启动服务器ServerSocketChannelserverSocketChannel=ServerSocketChannel.open();serverSocketChannel.socket().bind(newInetSocketAddress(9999));serverSocketChannel.configureBlocking(false);//获取选择器Selectorselector=Selector.open();serverSocketChannel.register(选择器,SelectionKey.OP_ACCEPT);int记录数=1;while(true){intreadyNum=selector.select();如果(readyNum<=0)继续;设置<选择键>选择键ys=selector.selectedKeys();Iteratoriterator=selectionKeys.iterator();while(iterator.hasNext()){SelectionKeykey=iterator.next();如果(key.isAcceptable()){ServerSocketChannelserverSocketChannel1=(ServerSocketChannel)key.channel();SocketChannelsocketChannel=serverSocketChannel1.accept();socketChannel.configureBlocking(false);//监听该SocketChannel的读事件socketChannel.register(selector,SelectionKey.OP_READ);}elseif(key.isReadable()){SocketChannelsocketChannel=(SocketChannel)key.channel();ByteBufferbyteBuffer=ByteBuffer.allocate(10);//打印读取到的内容while(socketChannel.read(byteBuffer)>0){byteBuffer.flip();而(乙yteBuffer.hasRemaining()){System.out.print((char)byteBuffer.get());}byteBuffer.clear();}//修改对该SocketChannel感兴趣的事件集读写事件socketChannel.register(selector,SelectionKey.OP_READ|SelectionKey.OP_WRITE);}elseif(key.isWritable()){SocketChannelsocketChannel=(SocketChannel)key.channel();ByteBufferbyteBuffer=ByteBuffer.wrap((":"+++recCount+";").getBytes());while(byteBuffer.hasRemaining()){socketChannel.write(byteBuffer);}//修改对该SocketChannel感兴趣的事件集为读取事件socketChannel.register(selector,SelectionKey.OP_READ);}迭代器.remove();}}}}优缺点优点:实现简单,无需处理并发问题缺点:单线程无法利用多个CPU的处理能力,性能2.2.2SingleReactorMultithreadingSingleReactorMultithreading下也只有一个Reactor。与单线程的主要区别在于多线程用于处理业务逻辑,如下图所示:一般流程Reactor监听客户端请求事件。如果是已建立的Connection事件,将事件分发给acceptor处理,acceptor将新的连接注册到Reactor。如果是读或者写事件,将事件分发给对应的handler处理,对应的handler会把事件交给线程池处理。示例代码处理事件的业务逻辑与单线程相同,只是将事件交给线程池处理/***说明:单Reactor多线程*kamier于2022年创建/6/1322:20*/publicclassSingleReactorMultiThread{publicstaticAtomicIntegerrecCount=newAtomicInteger();publicstaticvoidmain(String[]args)throwsIOException,InterruptedException{//创建线程池ThreadPoolExecutorthreadPoolExecutor=newThreadPoolExecutor(2,10,newCONuelock,LinkDS,LinkDS<>());//启动服务器ServerSocketChannelserverSocketChannel=ServerSocketChannel.open();serverSocketChannel.socket().bind(newInetSocketAddress(9999));serverSocketChannel.configureBlocking(false);//获取一个选择器Selectorselector=Selector.open();serverSocketChannel.register(选择器,SelectionKey.OP_ACCEPT);while(true){intreadyNum=selector.select();如果(readyNum<=0)继续;设置selectionKeys=selector.selectedKeys();CountDownLatchlatch=newCountDownLatch(selectionKeys.size());for(SelectionKeykey:selectionKeys){//提交任务给线程池threadPoolExecutor.submit(newSelectionKeyTask(key,selector,latch));}//这里等候本轮事件全部处理完成latch.await();selectionKeys.clear();}}privatestaticclassSelectionKeyTaskimplementsRunnable{SelectionKeykey;选择器选择器;CountDownLatch闩锁;SelectionKeyTask(SelectionKeykey,Selectorselector,CountDownLatchlatch){this.key=key;this.selector=选择器;this.latch=闩锁;}@Overridepublicvoidrun(){尝试{if(key.isAcceptable()){ServerSocketChannelserverSocketChannel1=(ServerSocketChannel)key.channel();SocketChannelsocketChannel=serverSocketChannel1.accept();socketChannel.configureBlocking(false);//监听该SocketChannel的读事件socketChannel.register(selector,SelectionKey.OP_READ);}elseif(key.isReadable()){SocketChannelsocketChannel=(SocketChannel)key.channel();ByteBufferbyteBuffer=ByteBuffer.allocate(10);//打印读取到的内容while(socketChannel.read(byteBuffer)>0){byteBuffer.flip();while(byteBuffer.hasRemaining()){System.out.print((char)byteBuffer.get());}byteBuffer.clear();}//修改对这个SocketChannel感兴趣的事件集来读写事件socketChannel.register(selector,SelectionKey.OP_READ|SelectionKey.OP_WRITE);}elseif(key.isWritable()){SocketChannelsocketChannel=(SocketChannel)键。渠道();ByteBufferbyteBuffer=ByteBuffer.wrap((":"+recCount.incrementAndGet()+";").getBytes());while(byteBuffer.hasRemaining()){socketChannel.write(byteBuffer);}//修改对该SocketChannel感兴趣的事件集为读取事件socketChannel.register(selector,SelectionKey.OP_READ);}}catch(Exceptione){e.printStackTrace();}最后{latch.countDown();}}}}优缺点优点:充分利用多CPU的处理能力缺点:实现相对复杂,需要解决多线程处理带来的并发问题。单个Reactor处理所有连接的IO事件。在大量连接的情况下,可能会出现性能问题2.2.3Master-slaveReactor多线程主从Reactor多线程是指有一个MainReactor和多个SubReactor。MainReactor监听连接建立事件,SubReactor用于监听注册到自己的连接的读写事件。如果请求事件是连接建立事件,则将该事件分发给acceptor处理,acceptor会将新的连接注册到某个SubReactor(可以有多个SubReactor),SubReactor会监听读写新连接的事件。如果注册给自己的连接的读写事件是read或者write事件,则将该事件分发给对应的handler处理,对应的handler将事件交给线程池处理。强缺点:实现复杂3.总结本文对Netty进行了简单的介绍,并对Reactor的三种模型进行了讲解。下面的章节会先介绍各个Netty组件的作用以及关键代码的解读,最后通过一个http服务器的例子,让我们看看整个服务器的启动过程是什么样子的,它是如何处理http请求的?