Netty网络框架
标签:
Netty网络框架Netty是一个异步的基于事件驱动的网络框架。
为什么要使用Netty而不直接使用JAVA中的NIO1.Netty支持三种IO模型同时支持三种Reactor模式。
2.Netty支持很多应用层的协议,提供了很多decoder和encoder。
3.Netty能够解决TCP长连接所带来的缺陷(粘包、半包等)
4.Netty支持应用层的KeepAlive。
5.Netty规避了JAVA NIO中的很多BUG,性能更好。
Netty启动服务端1.创建ServerBootstrap服务端启动对象。
2.配置bossGroup和workerGroup,其中bossGroup负责接收连接,workerGroup负责处理连接的读写就绪事件。
3.配置父Channel,一般为NioServerSocketChannel。
4.配置子Channel与Handler之间的关系。
5.给父Channel配置参数。
6.给子Channel配置参数。
7.绑定端口,启动服务。
private void start() { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap .group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // 配置父Channel .childHandler(new ChannelInitializer<SocketChannel>() { // 配置子Channel与Handler之间的关系 @Override protected void initChannel(SocketChannel socketChannel) { // 往ChannelPipeline中添加ChannelHandler socketChannel.pipeline().addLast( new HttpRequestDecoder(), new HttpObjectAggregator(65535), new HttpResponseEncoder(), new HttpServerHandler() ); } }) .option(ChannelOption.SO_BACKLOG, 128) // 给父Channel配置参数 .childOption(ChannelOption.SO_KEEPALIVE, true); // 给子Channel配置参数 try { // 绑定端口,启动服务 System.out.println("start server and bind 8888 port ..."); serverBootstrap.bind(8888).sync(); } catch (InterruptedException e) { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } Netty启动客户端1.创建Bootstrap客户端启动对象。
2.配置workerGroup,负责处理连接的读写就绪事件。
3.配置父Channel,一般为NioSocketChannel。
4.给父Channel配置参数。
5.配置父Channel与Handler之间的关系。
6.连接服务器。
private void start() { EventLoopGroup workerGroup = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); bootstrap.group(workerGroup) .channel(NioSocketChannel.class) // 配置父Channel .option(ChannelOption.SO_KEEPALIVE, true) // 给父Channel配置参数 .handler(new ChannelInitializer<SocketChannel>() { // 配置父Channel与Handler之间的关系 @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new TimeClientHandler()); } }); try { bootstrap.connect(new InetSocketAddress(8888)).sync(); // 连接服务器 } catch (InterruptedException e) { workerGroup.shutdownGracefully(); } } ChannelInBoundHandler接口声明了事件的处理方法 channelActive():当建立一个新的Channel时调用该方法 handlerAdd():当往Channel的ChannelPipeline中添加Handler时调用该方法 handlerRemove():当移除ChannelPipeline中的Handler时调用该方法 channelRead():当Channel有数据可读时调用该方法 exceptionCaught():当在处理事件发生异常时调用该方法ServerSocketChannel每接收到一个新的连接时都会建立一个SocketChannel,然后调用ChannelInitializer的init方法初始化Channel,方法中配置Channel与Handler之间的关系,然后调用Handler的handlerAdd()和channelActive()方法。
关于ChannelPipelineChannelPipeline底层使用双向链表。
当Channel有数据可读时,会沿着链表从前往后寻找有IN性质的Handler进行处理。
当Channel写入数据时,会沿着链表从后往前寻找有OUT性质的Handler进行处理。
关于write()和flush()方法graph TB; S1[Channel的write方法] --将数据写入到缓冲区--> buffer[缓冲区]; S2[Channel的flush方法] --发送缓冲区中的数据并清空--> buffer[缓冲区]; buffer --发送--> S3[SocketChannel];
write():将数据写入到缓冲区 flush():发送缓冲区中的数据并进行清空 writeAndFlush():将数据写入到缓冲区,同时发送缓冲区中的数据并进行清空Channel的writeAndFlush()和flush()方法会从链表的最后一个节点开始从后往前寻找有OUT性质的Handler进行处理。
ChannelHandlerContext的writeAndFlush()和flush()方法会从当前节点从后往前寻找有OUT性质的Handler进行处理。
关于写就绪事件当SocketChannel可以写入数据时,将会触发写就绪事件,所以一般不能随便监听,否则将会一直触发。
当SocketChannel在写入数据写不进时(缓冲区已经满了),向Selector传递要监听此Channel的写就绪事件,然后强制发送缓冲区中的数据并进行清空,此时将会触发写就绪事件,当Selector处理完写就绪事件后,应当剔除监听此Channel的写就绪事件。
为什么说Netty中的所有操作都是异步的Channel中的所有任务都会放入到其绑定的EventLoop的任务队列中,然后等待被EventLoop中的线程处理。
关于ChannelFuture由于Netty中的所有操作都是异步的,因此一般会返回ChannelFuture对象,用于存储Channel异步执行的结果。
当创建ChannelFuture实例时,isDone()方法返回false,仅当ChannelFuture被设置成成功或者失败时,isDone()方法才返回true。
可以往ChannelFuture中添加ChannelFutureListener,,当任务被执行完毕后由IO线程自动调用。
Netty中的ByteBufByteBuf有readerIndex和writerIndex两个指针,默认都为0,当进行写操作时移动writerIndex指针,读操作时移动readerIndex指针。
可读容量 = writerIndex - readerIndex
*只有read()/write()方法才会移动指针,get()/set()方法不会移动指针。
*ByteBuf支持动态扩容。
ByteBuf的创建和管理使用ByteBufAllocator来创建和管理ByteBuf,其分别提供PooledByteBufAllocator和UnpooledByteBufAllocator实现类,分别代表池化和非池化。
*Netty同时也提供了Pooled和Unpooled工具类来创建和管理ByteBuf。
池化的ByteBuf(Pooled)每次使用时都从池中取出一个ByteBuf对象,当使用完毕后再放回到池中。
每个ByteBuf都有一个refCount属性,仅当refCount属性为0时才将ByteBuf对象放回到池中。
ByteBuf的release()方法可以使refCount属性减1(一般由最后一个访问ByteBuf的Handler进行处理)
非池化的ByteBuf(Unpooled)每次使用时都创建一个新的ByteBuf对象。
使用池化ByteBuf的风险如果每次使用ByteBuf后却不进行释放,那么有可能发生内存泄漏,对象池中会不停的创建ByteBuf对象。
非池化的ByteBuf对象能够依赖JVM自动进行回收。
关于堆内和堆外的ByteBuf池化和非池化的ByteBufAllocator中都可以创建堆内和堆外的ByteBuf对象。
温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/web/41712.html