1、Socket和IO模型
1、Socket
Socket是两台主机之间逻辑连接的端点。TCP/IP是传输层协议,定义数据如何在忘了中进行传输。HTTP是应用成协议,主要用来定义规范,包装数据,方便数据处理。Socket是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议、本地主机的IP地址、本地进程的协议端口、远程主机的IP地址、远程进程的议
端口(IP+Port+Protocol)
2、Socket通信流程
3、IO模型
Java 共支持 3 种网络编程模型/IO 模式:BIO(同步并阻塞)、NIO(同步非阻塞)、AIO(异步非阻塞)
1、BIO(同步阻塞)
同步堵塞IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制优化
问题:
- 每个请求需要创建独立的线程,创建线程是需要JVM内存开销的,默认好像是1kb,如果说并发比较大,资源占用比较大(调小)
- 瞬时并发,请求回落,线程浪费严重(线程池)
- 线程在业务处理过程中,可能会碰到IO,消费者数据消费更不上生产者线程等等,这些势必会导致线程堵塞等待(reactor-stream)
- 连接建立后,当前线程中发现inputStram中没有数据可读,也会导致线程堵塞
NIO(同步非阻塞)
同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),以事件驱动为基础,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 I/O 请求就进行处理
缺点:
- NIO线程组需要指定大小,如果估算配置相差较大,也会造成浪费或不足
- 线程在业务处理过程中,可能会碰到IO,消费者数据消费更不上生产者线程等等,这些势必会导致线程堵塞等待(reactor-stream)
- 编写,理解代码相对来说复杂那么一点
AIO(异步非阻塞)
AIO 引入异步通道的概念,采用了 Proactor 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用
4、NIO编程
Java NIO 全称java non-blocking IO ,是指 JDK 提供的新 API。从 JDK1.4 开始,Java 提供了一系列改进的输入/输出的新特性,被统称为 NIO(即 New IO),是同步非阻塞的。
- NIO 有三大核心部分:Channel(通道),Buffer(缓冲区), Selector(选择器)
- NIO是 面向缓冲区编程的。数据读取到一个缓冲区中,需要时可在缓冲区中前后移动,这就增加了处理过程中的灵活性,使用它可以提供非阻塞式的高伸缩性网络
- Java NIO 的非阻塞模式,使一个线程从某通道发送请求或者读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入, 这个线程同时可以去做别的事情 通俗理解:NIO 是可以做到用一个线程来处理多个操作的。假设有 10000 个请求过来,根据实际情况,可以分配50 或者 100 个线程来处理。不像之前的阻塞 IO 那样,非得分配 10000 个
区别
- BIO是以流方式处理数据,NIO以缓冲区处理数据,缓冲区IO效率比流IO效率高,为啥??嘿嘿,等下说
- BIO是阻塞的,NIO是非阻塞的,内部通过事件驱动,taskQueue,Thread实现
- IO模型内部结构设计不一样,BIO是Thread-per-request,NIO是SocketChannel,Buffer,Selector
为什么缓冲区IO效率高于流IO?
- 流与块:
IO和NIO最重要的区别就是数据打包和传输的方式,IO以流的方式处理数据,而NIO以块的方式处理数据。
面向流的IO一次处理一个字节数据:一个输入流产生一个字节数据,一个输出流消费一个字节数据。为流式数据创建过滤器非常容易,连接一个过滤器,以便每个过滤器只负责复杂处理机制的一部分。不利的一面是,面向流的IO非常慢。
面向块的IO一次处理一个数据块,按照处理数据比按照流处理数据要块很多。但是面向块的IO缺少一些面向流的IO所具有的优雅性和简单性。
IO包和NIO已经很好的集合了,java.io. 已经以NIO为基础重新实现了,所以它现在可以利用NIO的一些特性。例如,java.io. 包中的一些类包含以快的形式读取数据的方法,这使得即使在面向流的系统中,处理速度也会更快。
- channel和stream:
方向性:channel数据是双向通行(read/write) ,stream是单向通行的。
channel是必须和buffer结合使用的,stream可以和buffer配套,也可以不用。
channel是可以设置为阻塞和非阻塞的。流本身就是阻塞的
NIO核心组件关系原理
- 每个 channel 都会对应一个 Buffer
- Selector 对应一个线程, 一个线程对应多个 channel(连接)
- 每个 channel 都注册到 Selector选择器上
- Selector不断轮询查看Channel上的事件, 事件是通道Channel非常重要的概念
- Selector 会根据不同的事件,完成不同的处理操作
- Buffer 就是一个内存块 , 底层是有一个数组
- 数据的读取写入是通过 Buffer, 这个和 BIO , BIO 中要么是输入流,或者是输出流, 不能双向,但是
NIO 的 Buffer 是可以读也可以写 , channel 是双向的.
#### 数据传输流程
Selector大致执行流程
只有在通道真正有读写事件发生时,线程才会进行读写,提高了线程利用率,减少上下文切换开销,避免创建多个线程处理导致内存开销
值得注意的对象和方法
Selector有几个重要方法需要注意:
- Selector.open() : //得到一个选择器对象
- selector.select() : //阻塞 监控所有注册的通道,当有对应的事件操作时, 会将SelectionKey放入集合内部并返回事件数量
- selector.select(1000): //阻塞 1000 毫秒,监控所有注册的通道,当有对应的事件操作时, 会将SelectionKey放入集合内部并返回
- selector.selectedKeys() : // 返回存有SelectionKey的集合
SelectionKey什么意思?
SelectionKey:A token representing the registration of a SelectableChannel with a Selector