关于netty:Netty源码解析-零拷贝机制与PoolArena

本文分享Netty中零拷贝机制与PoolArena的实现原理。 源码剖析基于Netty 4.1 Netty中的零拷贝首先看一下Netty中实现零拷贝的机制 1.文件传输类DefaultFileRegion#transferTo,调用FileChannel#transferTo,间接将文件缓冲区的数据发送到指标Channel,缩小用户缓冲区的拷贝(通过linux的sendfile函数)。 应用read 和 write DMA拷贝 拷贝,切换到用户态 拷贝,切换到内核态硬盘 --------> 内核缓冲区(内核态) ----------------> 用户缓冲区(用户态) ----------------> socket缓冲区 应用sendfile DMA拷贝 拷贝硬盘 --------> 内核缓冲区(内核态) ----> socket缓冲区 缩小用户态,内核态切换,以及数据拷贝 可参考: 操作系统和Web服务器那点事儿 2.Unpooled#wrappedBuffer办法,将byte数据,(jvm)ByteBuffer转换为ByteBufCompositeByteBuf#addComponents办法,合并ByteBufByteBuf#slice办法,提取ByteBuf中局部数据片段这些办法都是基于对象援用的操作,并没有内存拷贝 3.应用堆外内存(jvm)ByteBuffer对Socket读写。如果应用JVM的堆内存进行Socket读写,JVM会将堆内存拷贝一份到间接内存中,而后才写入Socket中。应用堆外内存能够防止该拷贝操作。留神,这里从内核缓冲区拷贝到用户缓冲区的操作并不能省略,毕竟咱们须要对数据进行操作,所以还是要拷贝到用户态的。可参考: 知乎--Java NIO中,对于DirectBuffer,HeapBuffer的疑难 接口关系ByteBufAllocator,内存分配器,负责为ByteBuf分配内存, 线程平安。PooledByteBufAllocator,默认的ByteBufAllocator,事后从操作系统中申请一大块内存,在该内存上分配内存给ByteBuf,能够进步性能和减小内存碎片。UnPooledByteBufAllocator,非池化内存分配器,每次都从操作系统中申请内存。 RecvByteBufAllocator,接管内存分配器,为Channel读入的IO数据调配一块大小正当的buffer空间。具体性能交由外部接口Handle定义。它次要是针对Channel读入场景增加一些操作,如guess,incMessagesRead,lastBytesRead等等。 ByteBuf,代表一个内存块,提供程序拜访和随机拜访,是一个或多个Byte数组或NIO Buffers的形象视图。ByteBuf次要能够分为堆外内存DirectByteBuf和堆内存HeapByteBuf。Netty4中ByteBuf调整为抽象类,从而晋升吞吐量。 上面只关注PooledByteBufAllocator,它是Netty中默认的内存调配策略(unsafe反对),也是了解Netty内存机制的难点。 内存调配后面文章《ChannelPipeline与Read,Write,Connect事件处理》中解析的read事件处理,NioByteUnsafe#read public final void read() { ... final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle(); allocHandle.reset(config); ByteBuf byteBuf = null; boolean close = false; ... byteBuf = allocHandle.allocate(allocator); allocHandle.lastBytesRead(doReadBytes(byteBuf)); ...}recvBufAllocHandle办法返回AdaptiveRecvByteBufAllocator.HandleImpl。(AdaptiveRecvByteBufAllocator,PooledByteBufAllocator都在DefaultChannelConfig中初始化) ...

September 13, 2020 · 4 min · jiezi

关于netty:netty-in-action学习笔记第四章

netty提供了对立的API进行传输数据,这个相比于JDK的形式不便很多。比方上面是一个不必netty而应用原生的阻塞IO进行传输的例子。 public class PlainOioServer { public void serve(int port) throws IOException { final ServerSocket socket = new ServerSocket(port); try { for(;;) { final Socket clientSocket = socket.accept(); System.out.println( "Accepted connection from " + clientSocket); new Thread(new Runnable() { @Override public void run() { OutputStream out; try { out = clientSocket.getOutputStream(); out.write("Hi!rn".getBytes( Charset.forName("UTF-8"))); out.flush(); clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } finally { try { clientSocket.close(); } catch (IOException ex) { // ignore on close } } } }).start(); } } catch (IOException e) { e.printStackTrace(); } }} 代码很好了解,为每一个新来的连贯创立一个线程解决。这种形式有个比拟大的问题是,客户端连接数受限于服务器所能接受的线程数。为了改良这个问题咱们能够应用异步模式来重写这段代码,然而你会发现,简直所有的代码都要重写。原生的OIO和NIO的API简直齐全不能复用。不信你看看上面这段NIO的代码, ...

September 9, 2020 · 3 min · jiezi

关于netty:netty-in-action学习笔记第二章-编写你的第一个netty程序

【netty in action】学习笔记-第二章 编写你的第一个netty程序 这一章简略粗犷,整个章节都是讲一个例子,例子很简略,然而麻雀虽小五脏俱全。通过这个示例你会对编写基于netty的应用程序有个直观的意识。 我先上代码,前面再剖析。 先看看服务端的示例, public class EchoServer { public int port; public EchoServer(int port) { this.port = port; } public void start() { EventLoopGroup group = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(group) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new EchoServerHandler()); } }); ChannelFuture f = b.bind().sync(); System.out.println(EchoServer.class.getName() + "started and listen on " + f.channel().localAddress()); f.channel().closeFuture().sync(); }catch (Exception e) { }finally { group.shutdownGracefully(); } } public static void main(String[] args) { new EchoServer(8888).start(); }}public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(Unpooled.copiedBuffer("netty rocks", CharsetUtil.UTF_8)); } @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf msg) throws Exception { ByteBuf recieveMsg=(ByteBuf) msg; String result = ByteBufUtil.hexDump(recieveMsg).toUpperCase();//将bytebuf中的可读字节 转换成16进制数字符串 String result2 = ByteBufUtil.hexDump(msg.readBytes(msg.readableBytes())); //看下两种形式输入的后果有什么区别 System.out.println("client received:" + result); System.out.println("client received:" + result2); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }}而后是客户端的示例, ...

August 30, 2020 · 2 min · jiezi

关于netty:netty-in-action学习笔记第一章-了解java-NIO2

【netty in action】学习笔记-第一章 理解java NIO(2) 上一篇文章理解了java nio的一些个性和根本用法。本篇持续来看看java nio有哪些问题以及netty如何解决这些问题。 跨平台和兼容性问题java nio有nio和nio2两个版本,后者只反对jdk7。而且java nio自身属于比拟low level的api,有时候会遇到在linux运行良好然而在windows上却有问题。 netty提供对立的api,你不须要关注java的版本,也不须要关注操作系统。 ByteBuffer的扩大通过后面的示例,你能看进去java nio的ByteBuffer并不好用,比方还有本人切换读写模式。netty扩大了ByteBuffer提供更加易用的API。具体的用法在前面章节的笔记中会具体阐明。 内存透露的问题nio有个Scattering and Gathering的概念,就是扩散读取,集中写入。 scatter(扩散)是指数据从一个channel读取到多个buffer中。比方上面的例子: ByteBuffer header = ByteBuffer.allocate(128);ByteBuffer body = ByteBuffer.allocate(1024);ByteBuffer[] bufferArray = { header, body }channel.read(bufferArray);read()办法依照buffer在数组中的程序将从channel中读取的数据写入到buffer,当一个buffer被写满后,channel紧接着向另一个buffer中写。 集中读的概念就是反过来,多个buffer的数据写入到同一个channel。示例如下: ByteBuffer header = ByteBuffer.allocate(128);ByteBuffer body = ByteBuffer.allocate(1024);ByteBuffer[] bufferArray = { header, body };channel.write(bufferArray);java nio提供了专门的接口来解决Scattering and Gathering, public interface ScatteringByteChannel extends ReadableByteChannel{ public long read(ByteBuffer[] dsts) throws IOException; public long read(ByteBuffer[] dsts, int offset, int length) throws IOException;}public interface GatheringByteChannel extends WritableByteChannel{ public long write(ByteBuffer[] srcs) throws IOException; public long write(ByteBuffer[] srcs, int offset, int length) throws IOException;}比方SocketChannel就实现了这两个接口。 ...

August 29, 2020 · 1 min · jiezi

关于netty:netty-in-action学习笔记第一章-了解java-NIO1

【netty in action】学习笔记-第一章 理解java NIO(1) 学习netty,java nio是根底,因为前者是对后者的封装,当然又不只是封装。随着学习的深刻你会了解这句话的含意。 下图是netty的架构图,让你对netty波及的模型,传输,协定有个根本印象。 netty的个性能够总结为一下几点: 统计的API操作阻塞和非阻塞的socket接口易用线程模型简略而弱小链式调用逻辑,复用性搞高文档和示例丰盛除了JDK之外不依赖别的组件相比于java api有更好的吞吐量以及低提早缩小不必要的内存占用,不会再因为疾速的,慢速的或者超负荷的连贯导致OOM反对SSL/TLS社区沉闷两种实现异步API罕用的设计 基于callbacks FetchCalback.java public interface FetchCalback { void onData(Data data); void onError(Throwable cause);}Fetcher.java public interface Fetcher { void fetchData(FetchCalback fetchCalback);}MyFetcher.java public class MyFetcher implements Fetcher{ @Override public void fetchData(FetchCalback fetchCalback) { try { //模仿获取数据 Thread.sleep(1000); Data data = new Data(1, 2); fetchCalback.onData(data); } catch (Exception e) { fetchCalback.onError(e); } }}Worker.java public class Worker { public void doWorker() { Fetcher fetcher = new MyFetcher(); fetcher.fetchData(new FetchCalback() { @Override public void onData(Data data) { System.out.println("获取到数据:" + data); } @Override public void onError(Throwable cause) { System.err.println(cause.getMessage()); } }); } public static void main(String[] args) { Worker worker = new Worker(); worker.doWorker(); }}Data.java ...

August 29, 2020 · 2 min · jiezi

关于netty:Netty实战-02手把手教你实现自己的第一个-Netty-应用新手也能搞懂

大家好,我是 「后端技术进阶」 作者,一个酷爱技术的少年。 @[toc] 感觉不错的话,欢送 star!( ´・・` )比心 Netty 从入门到实战系列文章地址:https://github.com/Snailclimb/netty-practical-tutorial 。RPC 框架源码地址:https://github.com/Snailclimb/guide-rpc-framework上面,我会带着大家搭建本人的第一个 Netty 版的 Hello World 小程序。 首先,让咱们来创立服务端。 服务端咱们能够通过 ServerBootstrap 来疏导咱们启动一个简略的 Netty 服务端,为此,你必须要为其指定上面三类属性: 线程组(_个别须要两个线程组,一个负责接解决客户端的连贯,一个负责具体的 IO 解决_)IO 模型(_BIO/NIO_)自定义 ChannelHandler (_解决客户端发过来的数据并返回数据给客户端_)创立服务端/** * @author shuang.kou * @createTime 2020年05月14日 20:28:00 */public final class HelloServer { private final int port; public HelloServer(int port) { this.port = port; } private void start() throws InterruptedException { // 1.bossGroup 用于接管连贯,workerGroup 用于具体的解决 EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { //2.创立服务端启动疏导/辅助类:ServerBootstrap ServerBootstrap b = new ServerBootstrap(); //3.给疏导类配置两大线程组,确定了线程模型 b.group(bossGroup, workerGroup) // (非必备)打印日志 .handler(new LoggingHandler(LogLevel.INFO)) // 4.指定 IO 模型 .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) { ChannelPipeline p = ch.pipeline(); //5.能够自定义客户端音讯的业务解决逻辑 p.addLast(new HelloServerHandler()); } }); // 6.绑定端口,调用 sync 办法阻塞晓得绑定实现 ChannelFuture f = b.bind(port).sync(); // 7.阻塞期待直到服务器Channel敞开(closeFuture()办法获取Channel 的CloseFuture对象,而后调用sync()办法) f.channel().closeFuture().sync(); } finally { //8.优雅敞开相干线程组资源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { new HelloServer(8080).start(); }}简略解析一下服务端的创立过程具体是怎么的: ...

August 27, 2020 · 3 min · jiezi

关于netty:Netty-01从-BIONIO-聊到-Netty最后还要实现个-RPC-框架

大家好,我是 「后端技术进阶」 作者,一个酷爱技术的少年。 @[toc] 感觉不错的话,欢送 star!( ´・・` )比心 Netty 从入门到实战系列文章地址:https://github.com/Snailclimb/netty-practical-tutorial 。RPC 框架源码地址:https://github.com/Snailclimb/guide-rpc-framework老套路,学习某一门技术或者框架的时候,第一步当然是要理解上面这几样货色。 是什么?有哪些特点?有哪些利用场景?有哪些胜利应用的案例?.....为了让你更好地理解 Netty 以及它诞生的起因,先从传统的网络编程说起吧! 还是要从 BIO 说起传统的阻塞式通信流程晚期的 Java 网络相干的 API(java.net包) 应用 Socket(套接字)进行网络通信,不过只反对阻塞函数应用。 要通过互联网进行通信,至多须要一对套接字: 运行于服务器端的 Server Socket。运行于客户机端的 Client SocketSocket 网络通信过程如下图所示: https://www.javatpoint.com/so... Socket 网络通信过程简略来说分为上面 4 步: 建设服务端并且监听客户端申请客户端申请,服务端和客户端建设连贯两端之间能够传递数据敞开资源对应到服务端和客户端的话,是上面这样的。 服务器端: 创立 ServerSocket 对象并且绑定地址(ip)和端口号(port): server.bind(new InetSocketAddress(host, port))通过 accept()办法监听客户端申请连贯建设后,通过输出流读取客户端发送的申请信息通过输入流向客户端发送响应信息敞开相干资源客户端: 创立Socket 对象并且连贯指定的服务器的地址(ip)和端口号(port):socket.connect(inetSocketAddress)连贯建设后,通过输入流向服务器端发送申请信息通过输出流获取服务器响应的信息敞开相干资源一个简略的 demo为了便于了解,我写了一个简略的代码帮忙各位小伙伴了解。 服务端: public class HelloServer { private static final Logger logger = LoggerFactory.getLogger(HelloServer.class); public void start(int port) { //1.创立 ServerSocket 对象并且绑定一个端口 try (ServerSocket server = new ServerSocket(port);) { Socket socket; //2.通过 accept()办法监听客户端申请, 这个办法会始终阻塞到有一个连贯建设 while ((socket = server.accept()) != null) { logger.info("client connected"); try (ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream())) { //3.通过输出流读取客户端发送的申请信息 Message message = (Message) objectInputStream.readObject(); logger.info("server receive message:" + message.getContent()); message.setContent("new content"); //4.通过输入流向客户端发送响应信息 objectOutputStream.writeObject(message); objectOutputStream.flush(); } catch (IOException | ClassNotFoundException e) { logger.error("occur exception:", e); } } } catch (IOException e) { logger.error("occur IOException:", e); } } public static void main(String[] args) { HelloServer helloServer = new HelloServer(); helloServer.start(6666); }}ServerSocket 的 accept() 办法是阻塞办法,也就是说 ServerSocket 在调用 accept()期待客户端的连贯申请时会阻塞,直到收到客户端发送的连贯申请才会持续往下执行代码,因而咱们须要要为每个 Socket 连贯开启一个线程(能够通过线程池来做)。 ...

August 27, 2020 · 3 min · jiezi

关于netty:Netty权威指南-第2版-带书签目录-完整版

Netty权威指南 第2版 带书签目录 完整版 下载地址: https://pan.baidu.com/s/12h96bKAdKEGXHdqOskK3nw 扫码上面二维码关注公众号回复100019 获取分享码 本书目录构造如下: 第1章 Java 的I/O 演进之路 1.1 I/O 根底入门 1.2 Java 的I/O 演进 1.3 总结 第2章 NIO 入门 2.1 传统的BIO 编程 2.2 伪异步I/O 编程 2.3 NIO 编程 2.4 AIO 编程 2.5 4 种I/O 的比照 2.6 抉择Netty 的理由 2.7 总结 入门篇 Netty NIO 开发指南 第3章 Netty 入门利用 3.1 Netty 开发环境的搭建 3.2 Netty 服务端开发 3.3 Netty 客户端开发 3.4 运行和调试 3.5 总结 ...

August 11, 2020 · 2 min · jiezi

关于netty:基于netty的基础小案例

server端代码import com.chinadaas.bio.chinadaasbio.netty.handler.ServerHandler;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;public class Server { public static void main(String[] args) throws InterruptedException { NioEventLoopGroup boosGroup = new NioEventLoopGroup(); NioEventLoopGroup workerGroup = new NioEventLoopGroup(); try { // 定义服务端启动类 ServerBootstrap start = new ServerBootstrap() .group(boosGroup, workerGroup) //设置两个线程组 .channel(NioServerSocketChannel.class) // 应用nioServer .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列失去的链接个数 .childOption(ChannelOption.SO_KEEPALIVE, true) // 设置放弃流动连贯状态 .childHandler(new ChannelInitializer<SocketChannel>() { // 创立一个通道测试对象(匿名对象) @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new ServerHandler()); } }); System.out.println("服务器 is ready"); // 绑定一个端口 并且同步生成一个ChannelFuture // 启动服务器(并绑定端口) ChannelFuture channelFuture = start.bind(8886).sync(); // 对敞开通道进行监听 channelFuture.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { boosGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }}client端代码import com.chinadaas.bio.chinadaasbio.netty.handler.ClientHandler;import io.netty.bootstrap.Bootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelInitializer;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioSocketChannel;public class Client { public static void main(String[] args) { NioEventLoopGroup workerGroup = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap() .group(workerGroup) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new ClientHandler()); } }); System.out.println("客户端 is ready"); ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8886).sync(); // 敞开通道进行监听 channelFuture.channel().closeFuture().sync(); }catch (Exception e){ e.printStackTrace(); }finally { workerGroup.shutdownGracefully(); } }}serverHandler 代码import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import io.netty.util.CharsetUtil;public class ServerHandler extends ChannelInboundHandlerAdapter { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("有异样"); ctx.close(); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf byteBuf = (ByteBuf) msg; System.out.println("服务器收到的音讯:" + byteBuf.toString(CharsetUtil.UTF_8)); System.out.println("客戶端的地址:" + ctx.channel().remoteAddress()); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端", CharsetUtil.UTF_8)); }}clientHandler 代码public class ClientHandler extends ChannelInboundHandlerAdapter { /** * 通道就绪后所触发的办法 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(Unpooled.copiedBuffer("hello 服务器", CharsetUtil.UTF_8)); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf byteBuf = (ByteBuf) msg; System.out.println("客户端收到音讯:" + byteBuf.toString(CharsetUtil.UTF_8)); System.out.println("服务器的地址:" + ctx.channel().remoteAddress()); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); }}

August 7, 2020 · 2 min · jiezi

关于netty:Netty之旅你想要的NIO知识点这里都有

高清思维导图原件(xmind/pdf/jpg)能够关注公众号:一枝花算不算浪漫 回复nio即可。 前言道歉好久没更原创文章了,看了下上篇更新工夫,曾经拖更一个多月了。 这段时间也始终在学习Netty相干常识,因为波及知识点比拟多,也走了不少弯路。目前网上对于Netty学习材料玲琅满目,不知如何下手,其实大家都是一样的,学习办法和技巧都是总结进去的,咱们在没有找到很好的办法之前不如循序渐进先从根底开始,个别从总分总的渐进形式,既观森林,又见草木。 之前凑巧跟杭州一个敌人小飞也提到过,两者在这方面的初衷是统一的,也心愿更多的敌人可能退出一起学习和探讨。(PS:本篇文章是和小飞一起学习整顿所得~) Netty是一款提供异步的、事件驱动的网络应用程序框架和工具,是基于NIO客户端、服务器端的编程框架。所以这里咱们先以NIO和依赖相干的根底铺垫来进行分析解说,从而作为Netty学习之旅的一个开始。 一、网络编程根底回顾1. SocketSocket自身有“插座”的意思,不是Java中特有的概念,而是一个语言无关的规范,任何能够实现网络编程的编程语言都有Socket。在Linux环境下,用于示意过程间网络通信的非凡文件类型,其本质为内核借助缓冲区造成的伪文件。既然是文件,那么天经地义的,咱们能够应用文件描述符援用套接字。 与管道相似的,Linux零碎将其封装成文件的目标是为了对立接口,使得读写套接字和读写文件的操作统一。区别是管道次要利用于本地过程间通信,而套接字多利用于网络过程间数据的传递。 能够这么了解:Socket就是网络上的两个应用程序通过一个双向通信连贯实现数据交换的编程接口API。 Socket通信的根本流程具体步骤如下所示: (1)服务端通过Listen开启监听,期待客户端接入。 (2)客户端的套接字通过Connect连贯服务器端的套接字,服务端通过Accept接管客户端连贯。在connect-accept过程中,操作系统将会进行三次握手。 (3)客户端和服务端通过write和read发送和接收数据,操作系统将会实现TCP数据的确认、重发等步骤。 (4)通过close敞开连贯,操作系统会进行四次挥手。 针对Java编程语言,java.net包是网络编程的根底类库。其中ServerSocket和Socket是网络编程的根底类型。 SeverSocket是服务端利用类型。Socket是建设连贯的类型。当连贯建设胜利后,服务器和客户端都会有一个Socket对象示例,能够通过这个Socket对象示例,实现会话的所有操作。对于一个残缺的网络连接来说,Socket是平等的,没有服务器客户端分级状况。 2. IO模型介绍对于一次IO操作,数据会先拷贝到内核空间中,而后再从内核空间拷贝到用户空间中,所以一次read操作,会经验两个阶段: (1)期待数据筹备 (2)数据从内核空间拷贝到用户空间 基于以上两个阶段就产生了五种不同的IO模式。 阻塞IO:从过程发动IO操作,始终期待上述两个阶段实现,此时两阶段一起阻塞。非阻塞IO:过程始终询问IO筹备好了没有,筹备好了再发动读取操作,这时才把数据从内核空间拷贝到用户空间。第一阶段不阻塞但要轮询,第二阶段阻塞。多路复用IO:多个连贯应用同一个select去询问IO筹备好了没有,如果有筹备好了的,就返回有数据筹备好了,而后对应的连贯再发动读取操作,把数据从内核空间拷贝到用户空间。两阶段离开阻塞。信号驱动IO:过程发动读取操作会立刻返回,当数据筹备好了会以告诉的模式通知过程,过程再发动读取操作,把数据从内核空间拷贝到用户空间。第一阶段不阻塞,第二阶段阻塞。异步IO:过程发动读取操作会立刻返回,等到数据筹备好且曾经拷贝到用户空间了再告诉过程拿数据。两个阶段都不阻塞。这五种IO模式不难发现存在这两对关系:同步和异步、阻塞和非阻塞。那么略微解释一下: 同步和异步同步: 同步就是发动一个调用后,被调用者未解决完申请之前,调用不返回。异步: 异步就是发动一个调用后,立即失去被调用者的回应示意已接管到申请,然而被调用者并没有返回后果,此时咱们能够解决其余的申请,被调用者通常依附事件,回调等机制来告诉调用者其返回后果。同步和异步的区别最大在于异步的话调用者不须要期待处理结果,被调用者会通过回调等机制来告诉调用者其返回后果。 阻塞和非阻塞阻塞: 阻塞就是发动一个申请,调用者始终期待申请后果返回,也就是以后线程会被挂起,无奈从事其余工作,只有当条件就绪能力持续。非阻塞: 非阻塞就是发动一个申请,调用者不必始终等着后果返回,能够先去干其余事件。阻塞和非阻塞是针对过程在拜访数据的时候,依据IO操作的就绪状态来采取的不同形式,说白了是一种读取或者写入操作方法的实现形式,阻塞形式下读取或者写入函数将始终期待,而非阻塞形式下,读取或者写入办法会立刻返回一个状态值。 如果组合后的同步阻塞(blocking-IO)简称BIO、同步非阻塞(non-blocking-IO)简称NIO和异步非阻塞(asynchronous-non-blocking-IO)简称AIO又代表什么意思呢? BIO (同步阻塞I/O模式): 数据的读取写入必须阻塞在一个线程内期待其实现。这里应用那个经典的烧开水例子,这里假如一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去解决下一个水壶。然而实际上线程在期待水壶烧开的时间段什么都没有做。NIO(同步非阻塞): 同时反对阻塞与非阻塞模式,但这里咱们以其同步非阻塞I/O模式来阐明,那么什么叫做同步非阻塞?如果还拿烧开水来说,NIO的做法是叫一个线程一直的轮询每个水壶的状态,看看是否有水壶的状态产生了扭转,从而进行下一步的操作。AIO(异步非阻塞I/O模型): 异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有IO操作的状态扭转,在相应的状态扭转后,零碎会告诉对应的线程来解决。对应到烧开水中就是,为每个水壶下面装了一个开关,水烧开之后,水壶会主动告诉我水烧开了。java 中的 BIO、NIO和AIO了解为是 Java 语言在操作系统层面对这三种 IO 模型的封装。程序员在应用这些 封装API 的时候,不须要关怀操作系统层面的常识,也不须要依据不同操作系统编写不同的代码,只须要应用Java的API就能够了。由此,为了使读者对这三种模型有个比拟具体和递推式的理解,并且和本文主题NIO有个清晰的比照,上面持续延长。 Java BIOBIO编程形式通常是是Java的上古产品,自JDK 1.0-JDK1.4就有的货色。编程实现过程为:首先在服务端启动一个ServerSocket来监听网络申请,客户端启动Socket发动网络申请,默认状况下SeverSocket会建设一个线程来解决此申请,如果服务端没有线程可用,客户端则会阻塞期待或受到回绝。服务器实现模式为一个连贯一个线程,即客户端有连贯申请时服务器端就须要启动一个线程进行解决。大抵构造如下: 如果要让 BIO 通信模型可能同时解决多个客户端申请,就必须应用多线程(次要起因是 socket.accept()、socket.read()、 socket.write() 波及的三个次要函数都是同步阻塞的),也就是说它在接管到客户端连贯申请之后为每个客户端创立一个新的线程进行链路解决,解决实现之后,通过输入流返回应答给客户端,线程销毁。这就是典型的 一申请一应答通信模型 。咱们能够构想一下如果这个连贯不做任何事件的话就会造成不必要的线程开销,不过能够通过线程池机制改善,线程池还能够让线程的创立和回收老本绝对较低。应用线程池机制改善后的 BIO 模型图如下: BIO形式实用于连贯数目比拟小且固定的架构,这种形式对服务器资源要求比拟高,并发局限于利用中,是JDK1.4以前的惟一抉择,但程序直观简略易懂。Java BIO编程示例网上很多,这里就不进行coding举例了,毕竟前面NIO才是重点。 Java NIONIO(New IO或者No-Blocking IO),从JDK1.4 开始引入的非阻塞IO,是一种非阻塞+ 同步的通信模式。这里的No Blocking IO用于辨别下面的BIO。 ...

August 7, 2020 · 5 min · jiezi

关于netty:Netty应用入门及重要组件

1.Netty简介Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以疾速开发高性能、高可靠性的网络服务器和客户端程序。 2.为什么应用Netty尽管 JAVA NIO 框架提供了多路复用 IO 的反对,然而并没有提供下层“信息格式”的良好封装。例如前两者并没有提供针对 Protocol Buffer、JSON 这些信息格式的封装,然而 Netty 框架提供了这些数据格式封装(基于责任链模式的编码和解码性能); 2、NIO 的类库和 API 相当简单,应用它来开发,须要十分熟练地把握 Selector、ByteBuffer、ServerSocketChannel、SocketChannel 等,须要很多额定的编程技能来辅助应用 NIO,例如,因为 NIO 波及了 Reactor 线程模型,所以必须必须对多线程和网络编程十分相熟能力写出高质量的 NIO 程序 3、要编写一个牢靠的、易保护的、高性能的 NIO 服务器利用。除了框架自身要兼容实现各类操作系统的实现外。更重要的是它应该还要解决很多下层特有服务,例如:客户端的权限、还有下面提到的信息格式封装、简略的数据读取,断连重连,半包读写,心跳等等,这些 Netty 框架都提供了响应的反对。 4、JAVA NIO 框架存在一个 poll/epoll bug:Selector doesn’t block on Selector.select(timeout),不能 block 意味着 CPU 的使用率会变成 100%(这是底层 JNI 的问题,下层要解决这个异样实际上也好办)。当然这个 bug 只有在 Linux内核上能力重现 3.Netty重要组件3.1 Channel接口在Java 的网络编程中,其根本的结构是类 Socket。Netty 的 Channel 接口所提供的 API,被用于所有的 I/O 操作。大大地升高了间接应用 Socket类的复杂性。此外,Channel也是领有许多预约义的、专门化实现的宽泛类层次结构的根。 channel生命周期ChannelUnregistered :Channel 曾经被创立,但还未注册EventLoopChannelRegistered :Channel 曾经被注册到了 EventLoopChannelActive :Channel 处于活动状态(曾经连贯到它的近程节点)。它当初能够接管和发送数据了ChannelInactive :Channel 没有连贯到近程节点当这些状态产生扭转时,将会生成对应的事件。这些事件将会被转发给 ChannelPipeline中的 ChannelHandler,其能够随后对它们做出响应。 ...

August 1, 2020 · 3 min · jiezi

关于netty:Netty-草稿

Reactor开发模式SocketChannel 在client端监听op_connect,op_write,op_read事件,在server只监听op_write,op_read事件,ServerSocketChannel在server端运行,只监听op_accept事件 netty对Reactor模式的实现首先ServerSocketChannel是能够创立子的socketchannel,创立是由BootstrapChannelFactory这个工厂类依据传入的class反射来创立,所以serverBootstrap.group().channel(xxx.class) .channel这个时候就是由下面的工厂类利用反射动静的创立一个channel,绑定在对应的group上 serverBootstrap.group(bossGroup,workerGroup) .channel传进来的会创立一个channel绑定在bossGroup上,而后传进来的ServerSocketChannel能够创立子socketchannel绑定在wokergroup上 再和下面的reactor开发模式对应,就是serversocketchannel绑定在bossGroup上来负责监听op_accept事件,socketchannel绑定在workergroup上来负责监听其余read write事件 粘包和半包 TCP为什么会呈现粘包和半包(UDP其实没有这个问题):次要是因为TCP是流协定 罕用的解决这种问题的计划:要么短链接,要么长链接中采纳封装成桢framing技术 netty对应用Framing粘包半包的反对:固定长度的形式:肯定就按固定长度的来传,不够就补空,解码FixedLengthFrameDecoder宰割符的形式:以指定的宰割符来宰割,但要思考内容中如果有这个宰割符须要本义,解码DelimiterBasedFrameDecoder固定长度字段存内容的长度信息:要思考预留多少位来存储这个长度,解码器是LengthFieldBasedFramedDecoder,编码器LengthFieldPrepender 下面的编码器都继承了ByteToMessageDecoder这个抽象类 ByteToMessageDecoder 继承 ChannelInboundHandlerAdapterChannelInboundHandlerAdapter 中有一个channelRead办法解决数据,ByteToMessageDecoder 中就具体实现了channelRead办法ByteToMessageDecoder中保护了一个ByteBuf类型的数据积攒器cumulation,如果是第一笔数据间接赋值给cumulation,不是第一笔的就追加在cumulation前面,而后调 callDecode(ctx, cumulation, out); //其中callDecode中会调 decodeRemovalReentryProtection(ctx, in, out); //而后这个又会调decode(ctx, in, out);//这个decode是ByteToMessageDecoder中提供的形象办法,具体由下面的各种Decoder来实现 以FixedLengthFrameDecoder为例子 public class FixedLengthFrameDecoder extends ByteToMessageDecoder { private final int frameLength; public FixedLengthFrameDecoder(int frameLength) { if(frameLength <= 0) { throw new IllegalArgumentException("frameLength must be a positive integer: " + frameLength); } else { this.frameLength = frameLength; } } protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { Object decoded = this.decode(ctx, in); if(decoded != null) { out.add(decoded);//每一次解进去的数据放在out中 } } protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { return in.readableBytes() < this.frameLength?null:in.readSlice(this.frameLength).retain(); //如果以后积攒器就是这里的in中的数据小于定义的长度,不做任何操作,这个时候数据也不够,超过定义的长度,取frameLength长度的数据进去,剩下的就还在积攒器中 }

July 31, 2020 · 1 min · jiezi

netty系列4Reactor模式转载

本文转载自《Reactor模式》。 1.为什么是Reactor模式写多了代码的兄弟们都知道,JAVA代码由于到处面向接口及高度抽象,用到继承多态和设计模式,程序的组织不是按照正常的理解顺序来的,对代码跟踪很是个问题。所以,在阅读别人的源码时,如果不了解代码的组织方式,往往是晕头转向,不知在何处。尤其是阅读经典代码的时候,更是如此。 反过来,如果先了解代码的设计模式,再来去代码,就会阅读的很轻松,不会那么难懂。 像netty这样的精品中的极品,肯定也是需要先从设计模式入手的。netty的整体架构,基于了一个著名的模式——Reactor模式。Reactor模式,是高性能网络编程的必知必会模式。 首先熟悉Reactor模式,一定是磨刀不误砍柴工。 2.Reactor模式简介Netty是典型的Reactor模型结构,关于Reactor的详尽阐释,本文站在巨人的肩膀上,借助 Doug Lea(就是那位让人无限景仰的大爷)的“Scalable IO in Java”中讲述的Reactor模式。 “Scalable IO in Java”的地址是:http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf Reactor模式也叫反应器模式,大多数IO相关组件如Netty、Redis在使用的IO模式,为什么需要这种模式,它是如何设计来解决高性能并发的呢? 3.多线程IO的致命缺陷最最原始的网络编程思路就是服务器用一个while循环,不断监听端口是否有新的套接字连接,如果有,那么就调用一个处理函数处理,类似: while(true){socket = accept();handle(socket)}这种方法的最大问题是无法并发,效率太低,如果当前的请求没有处理完,那么后面的请求只能被阻塞,服务器的吞吐量太低。 之后,想到了使用多线程,也就是很经典的connection per thread,每一个连接用一个线程处理,类似: package com.crazymakercircle.iodemo.base;import com.crazymakercircle.config.SystemConfig;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;class BasicModel implements Runnable { public void run() { try { ServerSocket ss = new ServerSocket(SystemConfig.SOCKET\_SERVER\_PORT); while (!Thread.interrupted()) new Thread(new Handler(ss.accept())).start(); //创建新线程来handle // or, single-threaded, or a thread pool } catch (IOException ex) { /\* ... \*/ } } static class Handler implements Runnable { final Socket socket; Handler(Socket s) { socket = s; } public void run() { try { byte\[\] input = new byte\[SystemConfig.INPUT\_SIZE\]; socket.getInputStream().read(input); byte\[\] output = process(input); socket.getOutputStream().write(output); } catch (IOException ex) { /\* ... \*/ } } private byte\[\] process(byte\[\] input) { byte\[\] output=null; /\* ... \*/ return output; } }}对于每一个请求都分发给一个线程,每个线程中都独自处理上面的流程。 ...

June 17, 2020 · 5 min · jiezi

netty系列1-netty框架介绍

1. 前言Netty 是一个异步的、基于事件驱动的网络应用框架,用以快速开发高性能、高可靠性的网络I0 程序,是由JBOSS提供的。Netty本质是- 个NIO框架,适用于服务器通讯相关的多种应用场景,主要针对在TCP协议下的使用。 我原本想自己介绍netty框架,但是碍于目前能力和精力有限,写出来的东西没有别人好。所以本文基本都是转载一位大神的文章《新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析》。还有一位叫正号先生写的《Netty学习笔记》,也受益颇多。我也在kindle上看过两本netty教程的书,但烟火气不够,看完后困惑更多,反倒是在博客里面看到的这些文章,实实在在的触动了我。 担心原作者哪天把博客里面的文章删了,我就再也找不到了,所以这里就开始无耻的抄袭。 2. jdk原生nio的问题DK 原生也有一套网络应用程序 API,但是存在一系列问题,主要如下: NIO 的类库和 API 繁杂,使用麻烦:你需要熟练掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer 等。需要具备其他的额外技能做铺垫:例如熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的 NIO 程序。可靠性能力补齐,开发工作量和难度都非常大:例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等。NIO 编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大。JDK NIO 的 Bug:例如臭名昭著的 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。官方声称在 JDK 1.6 版本的 update 18 修复了该问题,但是直到 JDK 1.7 版本该问题仍旧存在,只不过该 Bug 发生概率降低了一些而已,它并没有被根本解决。3. netty的特点Netty 对 JDK 自带的 NIO 的 API 进行了封装,解决了上述问题。Netty的主要特点有: 设计优雅:适用于各种传输类型的统一 API 阻塞和非阻塞 Socket;基于灵活且可扩展的事件模型,可以清晰地分离关注点;高度可定制的线程模型 - 单线程,一个或多个线程池;真正的无连接数据报套接字支持(自 3.1 起)。使用方便:详细记录的 Javadoc,用户指南和示例;没有其他依赖项,JDK 5(Netty 3.x)或 6(Netty 4.x)就足够了。高性能、吞吐量更高:延迟更低;减少资源消耗;最小化不必要的内存复制。安全:完整的 SSL/TLS 和 StartTLS 支持。社区活跃、不断更新:社区活跃,版本迭代周期短,发现的 Bug 可以被及时修复,同时,更多的新功能会被加入。4. netty高性能的原因一个高性能、异步事件驱动的NIO框架2.支持TCP、UDP和文件传输。 ...

May 31, 2020 · 4 min · jiezi