|
<p style="text-align: center;"><strong><span style="color: rgb(0, 209, 0);">Zebra项目知识介绍五四篇(一)——</span></strong></p><p style="text-align: center;"><strong><span style="color: rgb(0, 209, 0);">NIO 、 BIO 、 Buffer 、 Channel、Selector</span></strong></p><p style="text-align: center;"><span style="color: rgb(0, 0, 0);"><strong>===========================================================</strong></span></p><p style="text-align: left;"><strong><span style="color: rgb(0, 209, 0);">一、JAVA中的IO</span></strong><br /></p><p><span style="color: rgb(255, 79, 121);">1.BIO – BlockingIO</span></p><p> BIO,即java传统IO,面向字节流/字符流进行操作,采用了同步</p><p>阻塞机制,在进行读写操作时都会造成程序的阻塞。</p><p> 在高并发环境下不可避免的会遇到问题。如开启线程过多、cpu无意义的在多个线程间轮询。</p><p><br /></p><p><span style="color: rgb(255, 79, 121);">2.NIO -- NonBlockingIO</span></p><p> JAVA的NIO 是从JDK1.4开始提供的IO操作的API,面向通道和缓冲区进行操作,采用了同步非阻塞机制,可以设置采用非阻塞机制,从而在进行操作时不会产生阻塞。</p><p> </p><p><span style="color: rgb(255, 79, 121);">3.AIO -- AsynchronousIO</span></p><p> JAVA的AIO是从JDK7开始提哦概念股的IO操作API,采用异步非阻塞机制,使用回调的方式,真正实现了高效异步IO。</p><p> </p><p style="text-align: center;"><span style="color: rgb(255, 79, 121);"><strong>主要介绍NIO</strong></span><br /></p><p style="text-align: left;"><span style="color: rgb(0, 209, 0);"><strong>二、NIO</strong></span></p><p><span style="color: rgb(255, 79, 121);">1.概述</span></p><p><span style="color: rgb(255, 79, 121);"></span></p><p> JDK1.4 中提出的一套新的IO机制,区别与传统的BIO(Blocking IO)</p><p><span class="Apple-tab-span" style="white-space:pre;"> </span>BIO在高并发场景下遇到了一些无法解决的问题 ---- ACCEPT CONNECT READ WRITE</p><p><span class="Apple-tab-span" style="white-space:pre;"> </span>针对于每一个客户端都需要在服务器端创建对应线程来处理,线程开辟运行是非常耗费资源的,并且服务器所能支持的最大并发线程数量是有限的,所以当高并发到来时,服务器会存在性能瓶颈。</p><p><span class="Apple-tab-span" style="white-space:pre;"> </span>所以我们想到用少量的线程同时处理多个客户端的连接。而由于传统的BIO阻塞式操作的特点,这种工作方式是无法实现的。</p><p><span class="Apple-tab-span" style="white-space:pre;"> </span>BIO:面向流操作字节字符,具有方向性</p><p><span class="Apple-tab-span" style="white-space:pre;"> </span>InputStream OutputStream Reader Writer</p><p><span class="Apple-tab-span" style="white-space:pre;"> </span>NIO:面向通道操作缓冲区,可以双向传输数据</p><p><span class="Apple-tab-span" style="white-space: pre;"> </span>Channel<span class="Apple-tab-span" style="white-space: pre;"> </span>Buffer Selector</p><p> NIO面向通道和缓冲区进行工作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。 </p><p> NIO采用了异步的机制,当线程从通道中读取、写入数据时,这些方法并不会造成阻塞。NIO中引入了选择器机制,从而实现了一个选择器监听多个底层通道,减少了线程并发数。</p><p><br /></p><p><span style="color: rgb(255, 79, 121);">2.Channel通道</span></p><p><span style="color: rgb(0, 0, 0);"> NIO是基于Channel工作操作的是Buffer。</span></p><p><span style="color: rgb(0, 0, 0);"> Channel叫做通道,与Stream不同,可以双向的进行数据通信。</span></p><p> ①主要的通道有4个:</p><p> FileChannel – 文件操作</p><p> DatagramChannel – UDP网络操作</p><p> SocketChannel – TCP网络操作</p><p> ServerSocketChannel – TCP网络操作 </p><p><br /></p><p> ②通道的特点:</p><p> 既可以从通道中读取数据,也可以向通道中写入数据,即通道是双向的。</p><p> 通道可以异步的进行读写。</p><p> 通道总是将数据先写入一个Buffer或从一个Buffer中写出数据到通道。</p><p><br /></p><p> ③通道之间的连接:</p><p> transferFrom();</p><p> transferTo();</p><p> </p><p> ④FileChannel<br /></p><p> 无法直接创建一个FileChannel,</p><p> 可以通过InputStream、OutputStream、RandomAccessFile来获取一个FileChannel对象。 </p><p> 可以通过postion()方法获取FileChannel的当前位置,</p><p> 也可以通过postion()方法设置FileChannel的当前位置 </p><p> 可以通过truncate()方法截取一个文件,文件中指定长度后面的部分将被删除。</p><p><br /></p><p> ⑤DatagramChannel<br /></p><p> DatagramChannel channel = DatagramChannel.open();</p><p> channel.socket().bind(new InetSocketAddress(9999));</p><p> ByteBuffer buf = ….</p><p> channel.receive(buf);//如果收到的数据大于buf,则多出的数据将被抛弃</p><p> .....</p><p> channel.send(buf,new InetSocketAddress(“127.0.0.1”,8888)); </p><p> channel.connect(new InetSocketAddress(..));//锁住当前UTP端,后续所有的 UDP操作都绑定为和这个地址进行,但是仍然保持UDP不保证数据到达性。</p><p> </p><p> ⑥ServerSocketChannel</p><p><span class="Apple-tab-span" style="white-space:pre;"> </span>ServerSocketChannel ssc = ServerSocketChannel.open();</p><p><span class="Apple-tab-span" style="white-space:pre;"> </span>configureBlocking(false);//设置为非阻塞模式</p><p><span class="Apple-tab-span" style="white-space:pre;"> </span>SocketChannel </p><p><span class="Apple-tab-span" style="white-space:pre;"> </span>SocketChannel sc = SocketChannel.open();</p><p><span class="Apple-tab-span" style="white-space:pre;"> </span>configureBlocking(false);//设置为非阻塞模式</p><p><br /></p><p> 在非阻塞模式下,进行ACCEPT CONNECT READ WRITE 操作时都不会产生阻塞,它们只是尝试完成操作,并不保证操作一定执行完。所以通常我们需要自己控制循环来保证操作执行完成。</p><p><br /></p><p><span style="color: rgb(255, 79, 121);">3.Buffer </span></p><p> 缓冲区本质上是一块内存区域,<span style="color: rgb(0, 0, 0);">用来临时存放数据</span><span style="color: rgb(255, 79, 121);">,</span>可以向其中写入/读取数据,用来实现数据的保持、共享、处理。</p><p> ①主要的Buffer(7个):</p><p> ByteBuffer</p><p> ShortBuffer</p><p> IntBuffer</p><p> LongBuffer</p><p> FloatBuffer</p><p> DoubleBuffer</p><p> CharBuffer</p><p> </p><p> ②缓冲区的属性</p><p> capacity – 容量</p><p> position – 当前位</p><p> limit – 限制位</p><p><br /></p><p> ③缓冲区在读取/写入时以上三种属性的意义有所不同:</p><p> capacity指定缓冲区的大小,指定缓冲区中可以存放多少个byte、long、char等取决于缓冲区类型的数据。</p><p> position指定缓冲区当前位置。在写入时,将向position位置写入数据。当写入一个单位的数据时,postion+1指向下一个写入位置。在读取数据时, position表示当前读取位置,每读取一个数据postion+1,向后移动指向下一个读取位置。</p><p> limit指定缓冲区的限制位置。在写时,limit指定最多写入到哪个位置。在读取时,limit指定最多读取到哪个位置。</p><p><span style="color: rgb(255, 79, 121);">可以通过如下方法获取、设置如上三个值:</span></p><table><tbody><tr><td width="152" valign="top" style="padding: 3px;border-width: 1px;border-style: outset;border-color: windowtext;background: rgb(255, 255, 255);"><p>static ByteBuffer</p></td><td width="197" valign="center" style="padding: 3px;border-left: none;"><p>allocate(int capacity) <br />分配一个新的字节缓冲区。</p></td></tr><tr><td width="152" valign="top" style="padding: 3px;border-left: 1px outset windowtext;border-right: 1px outset windowtext;border-top: none;"><p> int</p></td><td width="197" valign="center" style="padding: 3px;border-left: none;"><p>capacity() <br /> 返回此缓冲区的容量。</p></td></tr><tr><td width="152" valign="top" style="padding: 3px;border-left: 1px outset windowtext;border-right: 1px outset windowtext;border-top: none;"><p> int</p></td><td width="182" valign="center" style="padding: 3px;border-left: none;"><p>position()</p><p>返回此缓冲区的位置。</p></td></tr><tr><td width="152" valign="top" style="padding: 3px;border-left: 1px outset windowtext;border-right: 1px outset windowtext;border-top: none;"><p> Buffer</p></td><td width="182" valign="center" style="padding: 3px;border-left: none;"><p>position(int newPosition) <br />设置此缓冲区的位置。</p></td></tr><tr><td width="152" valign="top" style="padding: 3px;border-left: 1px outset windowtext;border-right: 1px outset windowtext;border-top: none;"><p>int</p></td><td width="197" valign="center" style="padding: 3px;border-left: none;"><p>limit() <br />返回此缓冲区的限制。</p></td></tr><tr><td width="152" valign="top" style="padding: 3px;border-left: 1px outset windowtext;border-right: 1px outset windowtext;border-top: none;"><p> Buffer</p></td><td width="197" valign="center" style="padding: 3px;border-left: none;"><p>limit(int newLimit) <br />设置此缓冲区的限制。</p></td></tr></tbody></table><p>另外还有其他的一些方法用来操作缓冲区位置关系:</p><table><tbody><tr><td width="56" valign="top" style="padding: 3px;border-width: 1px;border-style: outset;border-color: windowtext;background: rgb(255, 255, 255);"><p>Buffer</p></td><td width="193" valign="center" style="padding: 3px;border-left: none;"><p>rewind() </p><p>重绕此缓冲区。</p></td></tr></tbody></table><p>使缓冲区为重新读取已包含的数据做好准备:它使limit保持不变,将position设置为 0。</p><table><tbody><tr><td width="72" valign="top" style="padding: 3px;border-width: 1px;border-style: outset;border-color: windowtext;background: rgb(255, 255, 255);"><p> Buffer</p></td><td width="186" valign="center" style="padding: 3px;border-left: none;word-break: break-all;"><p>clear() </p><p>清除此缓冲区。</p></td></tr></tbody></table><p>它将limit设置为capacity大小,将postion设置为 0。</p><table><tbody><tr><td width="56" valign="top" style="padding: 3px;border-width: 1px;border-style: outset;border-color: windowtext;background: rgb(255, 255, 255);"><p>Buffer</p></td><td width="180" valign="center" style="padding: 3px;border-left: none;"><p>flip() </p><p>反转此缓冲区。</p></td></tr></tbody></table><p>它将limit设置为postion位置,然后将postion设置为 0。</p><table><tbody><tr><td width="176" valign="top" style="padding: 3px;border-width: 1px;border-style: outset;border-color: windowtext;background: rgb(255, 255, 255);"><p>abstract ByteBuffer</p></td><td width="215" valign="center" style="padding: 3px;border-left: none;"><p>compact() </p><p>压缩此缓冲区(可选操作)。</p></td></tr></tbody></table><p> 此方法将所有未读取的数据拷贝到Buffer起始处,然后将position设置到最后一个未读取元素后面。limit设置为和capacity一致。</p><p> </p><p> ④写入和读取数据到Buffer</p><p> 从Channel写入Buffer:通过Buffer的put()方法写入数据到Buffer。</p><p> 从Buffer读取数据到Channel:使用get()方法从Buffer中读取数据。 </p><p><span style="color: rgb(255, 79, 121);">其他方法:</span></p><table><tbody><tr><td width="144" valign="top" style="padding: 3px;border-width: 1px;border-style: outset;border-color: windowtext;background: rgb(255, 255, 255);"><p> Buffer</p></td><td width="339" valign="center" style="padding: 3px;border-left: none;"><p>mark() <br />在此缓冲区的位置设置标记。</p></td></tr><tr><td width="144" valign="top" style="padding: 3px;border-left: 1px outset windowtext;border-right: 1px outset windowtext;border-top: none;"><p>Buffer</p></td><td width="323" valign="center" style="padding: 3px;border-left: none;"><p>reset() <br />将此缓冲区的位置重置为以前标记的位置。</p></td></tr><tr><td width="144" valign="top" style="padding: 3px;border-left: 1px outset windowtext;border-right: 1px outset windowtext;border-top: none;"><p>int</p></td><td width="339" valign="center" style="padding: 3px;border-left: none;"><p>remaining() <br />返回当前位置与限制之间的元素数。</p></td></tr><tr><td width="144" valign="top" style="padding: 3px;border-left: 1px outset windowtext;border-right: 1px outset windowtext;border-top: none;"><p>abstract Object</p></td><td width="339" valign="center" style="padding: 3px;border-left: none;"><p>array() <br />返回此缓冲区的底层实现数组(可选操作)。</p></td></tr></tbody></table><p> </p><p> ⑤分散和聚集</p><p> ByteBuffer[] bufArray = {buf1,buf2}</p><p> channel.read(bufArray);</p><p> </p><p> ByteBuffer[] bufArray={buf1,buf2}</p><p> channel.write(bufArray);</p><p> 注意read和writer方法都无法保证是否完整的读取或写出,需要循环检查。</p><p> </p><p><span style="color: rgb(255, 79, 121);">4.Selector</span></p><p> ①在一个Selector上可以同时注册多个非阻塞的通道,从而只需要很少的线程数就可以管理许多通道。特别适用于开启许多通道但是每个通道中数据流量都很低的情况。</p><p> Selector selector = Selector.open();</p><p> channel.register(selector,SelectionKey.OP_READ);//要注意,只有非阻塞模式下的channel能应用在Selector上</p><p> </p><p> ②SelectionKey中的四种事件:</p><p> connect</p><p> accept</p><p> read</p><p> write</p><p> </p><p> ③SelectionKey中可以获取的数据:</p><p> interest集合— 感兴趣的事件集合:</p><p> 通过位与操作interest集合和给定的SelectionKey可以获知确定事件 是否在interest集合中。</p><p> read集合 – 就绪事件集合:</p><p> 通过位与操作read集合和给定的SelectionKey可以获知确定事件是否 在interest集合中。</p><p> <span style="color: rgb(255, 79, 121);">但通常使用更方便的操作:</span></p><p> isAcceptable()</p><p> isConnectable()</p><p> isReadable()</p><p> isWritable()</p><p> channel:key.channel()</p><p> selector:key.selector()</p><p> 附加对象</p><p> channel.register(selc,SelectionKey..,obj)</p><p> key.attach(obj);</p><p> obj = key.attachment();</p><p><br /></p><p> ④一些方法:</p><p> SelectionKey register(Selector sel, int ops);</p><p> //将当前通道注册到选择器中。sel:要被注册到的选择器。ops注册时指定的要关注的操作,操作可以有四种类型 - SelectionKey.OP_ACCEPT SelectionKey.OP_CONNECT SelectionKey.OP_READ SelectionKey.OP_WRITE </p><p> </p><p> Selector.open(); //获取选择器</p><p><span class="Apple-tab-span" style="white-space: pre;"> </span><br /></p><p> channle.regeister(selc,ops)</p><p> //将通道注册到选择器中让选择器管理这个通道</p><p><span class="Apple-tab-span" style="white-space: pre;"> </span><br /></p><p> int select() ;//检查注册在选择器上的通道们关心的事件是否已经有就绪可以处理的,如果有此方法返回,返回值为可以处理的数量,如果没有可处理的则此方法阻塞。</p><p><span class="Apple-tab-span" style="white-space: pre;"> </span><br /></p><p> Set<SelectionKey> selectedKeys();//获取已经就绪的键的集合</p><p><br /></p><p><span style="color: rgb(255, 79, 121);">5.粘包问题</span></p><p> 由于TCP传输是一种可靠的连续的数据传输,如果两次传输的数据时间间隔比较短,数据的接收方可能很难判断出两次数据的边界在哪里,感觉就好像两个数据黏着在了一次,无法区分。</p><p> <span style="text-decoration: underline;"> 解决方案1:</span>传输固定大小的数据,缺点是,很不灵活,有可能浪费传输空间。</p><p> <span style="text-decoration: underline;"> 解决方案2:</span>约定一个特殊的字符作为判断的边界,缺点是如果数据中本身就包含这个特殊字符可能还需要进行转义的操作。</p><p> <span style="text-decoration: underline;">解决方案3</span>:使用协议,通过协议传输数据量大小的方式来解决(HTTP Content-Length)。</p><p><br /></p><p><span style="color: rgb(255, 79, 121);">6.开源的NIO结构的服务器框架:</span> MINA NETTY</p><p><br /></p><p><span style="color: rgb(255, 79, 121);">7.总结</span></p><p> 阻塞/非阻塞:是从线程的角度来考虑的,考虑线程是不是被挂起的状态。</p><p> 同步/异步:是从逻辑执行的角度来考虑的,考虑程序在处理一段逻辑时可否并行处理另一端逻辑。</p><p> BIO -- jdk1.0 -- BlockingIO -- 同步阻塞式IO -- 面向流操作字节字符 -- 单向传输。</p><p> NIO -- jdk1.4 -- NonBlockingIO -- 同步非阻塞式IO -- 面向通道操作缓冲区 -- 双向传输。</p><p> AIO -- jdk1.7 -- AnsyncronizeIO -- 异步非阻塞式IO -- 大量的使用回调函数实现了异步IO操作。</p><p style="max-width: 100%;min-height: 1em;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><trans src="Apache Phoenix is fully integrated with other Hadoop products such as Spark, Hive, Pig, Flume, and Map Reduce." style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(0, 82, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;" /></span></trans></p><p style="max-width: 100%;min-height: 1em;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-family: 宋体;font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></span></span></p><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><hr style="max-width: 100%;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;" /><p style="max-width: 100%;min-height: 1em;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;line-height: 25.6px;color: rgb(0, 128, 255);box-sizing: border-box !important;word-wrap: break-word !important;">请扫描下面二维码,关注该微信公众号,获取更多</span><span style="max-width: 100%;line-height: 25.6px;color: rgb(255, 41, 65);box-sizing: border-box !important;word-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;">生物医学工程</strong>专业及<strong style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;">医工</strong><span style="max-width: 100%;line-height: 25.6px;color: rgb(0, 128, 255);box-sizing: border-box !important;word-wrap: break-word !important;">学习笔记</span>:</span><span style="max-width: 100%;color: rgb(0, 0, 0);box-sizing: border-box !important;word-wrap: break-word !important;"></span></p><p style="max-width: 100%;min-height: 1em;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;line-height: 25.6px;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;line-height: 25.6px;color: rgb(255, 41, 65);box-sizing: border-box !important;word-wrap: break-word !important;"></span></p><p style="max-width: 100%;min-height: 1em;color: rgb(62, 62, 62);font-size: 16px;white-space: normal;line-height: 25.6px;text-align: center;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;"><img class="" data-ratio="1" data-s="300,640" src="http://mmbiz.qpic.cn/mmbiz_jpg/LKgI6UN8ElZnh0jXF3PjbBz7BBPnYH7wbDgfc3E2r7REMuN2lQqfuVM38HZO8Zx0rN0OLKywrjoH3P7QqDY44Q/640?wx_fmt=jpeg" data-type="jpeg" data-w="430" style="box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;width: auto !important;" width="auto" /></p><p><br /></p>
|
|