如何能去医疗网站做编辑,手机网站建设视频,兴化网站制作,重庆市企业网站建设一、网络通信 1、什么是socket#xff1f; Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层#xff0c;它是一组接口#xff0c;一般由操作 系统提供。客户端连接上一个服务端#xff0c;就会在客户端中产生一个 socket 接口实例#xff0c;服务端每接受 一个客户端…一、网络通信 1、什么是socket Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层它是一组接口一般由操作 系统提供。客户端连接上一个服务端就会在客户端中产生一个 socket 接口实例服务端每接受 一个客户端连接就会产生一个 socket 接口实例和客户端的 socket 进行通信有多个客户 端连接自然就有多个 socket 接口实例。 2、网络编程中的serversocket与socket 在网络编程中应用程序启动会使用serversocket监听端口客户端通过ip与端口找到对应的应用程序建立连接后应用程序会创建对应的socket读取客户端传输的数据并通过socket写入数据后返回给客户端
二、JDK中的BIO BIBIO中的SercerSocket负责绑定IP启动监听端口等待客户端链接客户端的Socket累实例发起链接操作serversocket接收后产生一个新的服务端socket负责与客户端实例通过输入流和输出流进行通信 阻塞体现1、服务启动就绪主线程一直等待客户端链接主线程阻塞 2、建立连接后在读取socket信息之前线程也是一直阻塞等待
传统BIO模型 当客户端访问数量增加线程与客户端访问数量1:1服务端会创建大量线程线程数量增加系统性能急剧下降过多会导致系统宕机
为了改进这一问题我们可以采用n个线程处理多个客户端请求这一方式会导致多个客户端等待这是最大的弊端
三、什么是NIO
NIO是为了弥补BIO的不足提供了高速的、面向块的I/O。NIO全程NO-Blocking io
Java NIO 和 IO 之间第一个最大的区别是IO 是面向流的NIO 是面向缓冲区的。 Java IO 面向流意味着每次从流中读一个或多个字节直至读取所有字节它们没有被缓存在任何地 方。此外它不能前后移动流中的数据。如果需要前后移动从流中读取的数据需要先将它 缓存到一个缓冲区。 Java NIO 的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓 冲区需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是还需要检查 是否该缓冲区中包含所有需要处理的数据。而且需确保当更多的数据读入缓冲区时不要 覆盖缓冲区里尚未处理的数据。
1、阻塞与非阻塞IO
java IO的各种流是阻塞的这意味着当一个线程调用read或write时该线程呗阻塞
java NIO的非阻塞模式使一个线程从某通道发送请求读取数据牡丹石他仅能得到目前可用的数据如果没有数据就什么都读取不到而不是保持阻塞所以直到数据变更前可以做其它的事情因此是非阻塞
2、NIO之Reactor反应器模式
“反应”即“倒置”“控制逆转”,具体事件处理程序不调用反应器而向反应器注 册一个事件处理器表示自己对某些事件感兴趣有时间来了具体事件处理程序通过事件 处理器对某个指定的事件发生做出反应这种控制逆转又称为“好莱坞法则”不要调用我 让我来调用你 3、NIO的三大组件 Selector选择器、channel管道、buffer缓冲区
Channels管道通道应用程序与操作系统交互事件与传递内容的渠道应用程序可以通过通道来读写数据 a、所有被Selector注册的通道只能是继承了SelectableChannel类的子类 b、ServerSocketChannel应用程序的监听的通道只有通过这个通道应用程序才能向操作系统注册支持多路复用IO的端口监听 c、SocketChannelTCP Socket套接字的监听通道一个socket套接字对应了一个客户端IP端口服务端ip端口
Selectorjava的NIO选择器允许一个单独的线程来监视多个输入通道即多个通道可以使用一个选择器这个单独的线程可以操作这个选择器选择通道这种机制可以是的一个单独的线程很容易来管理多个通道
应用程序回想selector注册需要它关注的channel以及具体的channel会对那些IO事件感兴趣
Buffer缓冲区用于和NIO通道进行交互数据从通道读入缓冲区从缓冲区写入通道中本质是一块可以写入数据读取数据的内存就是包装成了NIO Buffer对象封装了该内存的访问方法
// 创建选择器
Selector SelectorSelector.open()
// 打开监听通道
serverChannel ServerSocketChannel.open();
// 开启非阻塞模式
serverChannel.configureBlcokfalse
// 绑定端口 backlog设为1024
serverChannel.socket.bind(new InetSocketAddress(port),1024);
// 注册监听监听客户端连接请求 SelectionKey.OP_READ读事件 SelectionKey.OP_WRITE写事件
SelectionKey key channel.register(selector,SelectionKey.OP_READ);
4、什么是SelectionKey SelectionKey是一个抽象类,表示 selectableChannel 在 Selector 中注册的标识.每个 Channel 向 Selector 注册时,都将会创建一个 SelectionKey。SelectionKey 将 Channel 与 Selector 建立了 关系并维护了 channel 事件。
SelectionKey的类型
操作类型就绪条件及说明OP_READ读当操作系统读缓冲区有数据可读时就绪。并非时刻都有数据可读所以一般需要注册该操作仅当有就绪时才发起读操作有的放矢避免浪费 CPU。OP_WRITE写当操作系统写缓冲区有空闲空间时就绪。一般情况下写缓冲区都有空闲空间小块数据直接写入即可没必要注册该操作类型否则该条件不断就绪浪费 CPU但如果是写密集型的任务比如文件下载等缓冲区很可能满注册 该操作类型就很有必要同时注意写完后取消注册。OP_CONNECT请求连接当 SocketChannel.connect()请求连接成功后就绪。该操作只给客户端使用。OP_ACCEPT接受连接当接收到一个客户端连接请求时就绪。该操作只给服务器使用。 服务端与客户端感兴趣的类型
OP_READOP_WRITEOP_CONNECTOP_ACCEPT服务器 ServerSocketChannel√服务器 SocketChanne√√客户端 SocketChannel√√√
服务器启动 ServerSocketChannel关注 OP_ACCEPT 事件 客户端启动 SocketChannel连接服务器关注 OP_CONNECT 事件 服务器接受连接启动一个服务器的 SocketChannel这个 SocketChannel 可以关注 OP_READ、OP_WRITE 事件一般连接建立后会直接关注 OP_READ 事件 客户端这边的客户端 SocketChannel 发现连接建立后可以关注 OP_READ、OP_WRITE 事件一般是需要客户端需要发送数据了才关注 OP_READ 事件 连接建立后客户端与服务器端开始相互发送消息读写根据实际情况来关注 OP_READ、 OP_WRITE 事件。
5、Buffer中的概念 capactiyBuffer内存块固定的大小值成为capactiy只能往里面写byte、long、char等类型buffer满了需要将其清空才能继续往里面写数据 position表示当前能写以及可读的位置 limit写模式下limit表示最多能写多少数据等于capacity 读模式下表示最多能读多少数据读模式下limit等于写模式下的position Buffer的分配想要获得一个buffer首先要进行分配可以在堆上分配也可以在直接内存
6、直接内存 直接内存不是运行在虚拟机的数据区也不是java定义的内存区域是系统的内存区域
NIO可以使用Native函数直接分配堆外内存然后通过一个存储在java堆里的DirectByteBuffer对象作为这块内存的引用进行操作可以避免java堆与Natice堆中来回复制数据
直接内存的申请更加耗费性能但读写性能要优于普通堆内存
7、Reactor模式类型 单线程Reactor模式流程
a、服务器端的 Reactor 是一个线程对象该线程会启动事件循环并使用 Selector(选择器)来实现 IO 的多路复用。注册一个 Acceptor 事件处理器到 Reactor 中Acceptor 事件处理器所关注的事件是 ACCEPT 事件这样 Reactor 会监听客户端向服务器端发起的连接请求事件(ACCEPT 事件)。
b、客户端向服务器端发起一个连接请求Reactor 监听到了该 ACCEPT 事件的发生并将 该 ACCEPT 事件派发给相应的 Acceptor 处理器来进行处理。Acceptor 处理器通过 accept()方法得到与这个客户端对应的连接(SocketChannel)然后将该连接所关注的 READ 事件以及对 应的 READ 事件处理器注册到 Reactor 中这样一来 Reactor 就会监听该连接的 READ 事件了。
c、当 Reactor 监听到有读或者写事件发生时将相关的事件派发给对应的处理器进行处理。比如读处理器会通过 SocketChannel 的 read()方法读取数据此时 read()操作可以直接读取到数据而不会堵塞与等待可读的数据到来。
d、每当处理完所有就绪的感兴趣的 I/O 事件后Reactor 线程会再次执行 select()阻塞等待新的事件就绪并将其分派给对应处理器进行处理。
注意Reactor 的单线程模式的单线程主要是针对于 I/O 操作而言也就是所有的 I/O 的 accept()、read()、write()以及 connect()操作都在一个线程上完成的。
但在目前的单线程 Reactor 模式中不仅 I/O 操作在该 Reactor 线程上连非 I/O 的业务 操作也在该线程上进行处理了这可能会大大延迟 I/O 请求的响应。所以我们应该将非 I/O 的业务逻辑操作从 Reactor 线程上卸载以此来加速 Reactor 线程对 I/O 请求的响应。 单线程Reactor工作者线程池
与单线程 Reactor 模式不同的是添加了一个工作者线程池并将非 I/O 操作从 Reactor线程中移出转交给工作者线程池来执行。这样能够提高 Reactor 线程的 I/O 响应不至于因为一些耗时的业务逻辑而延迟对后面 I/O 请求的处理。
优势a、线程重复利用 b、核心线程提前创建好不用等待线程创建 c、线程池大小可以调节更好的利用处理器
多线程主从Reactor模式
Reactor 线程池中的每一 Reactor 线程都会有自己的 Selector、线程和分发的事件循环逻 辑。mainReactor 可以只有一个但 subReactor 一般会有多个。mainReactor 线程主要负责接 收客户端的连接请求然后将接收到的 SocketChannel 传递给 subReactor由 subReactor 来 完成和客户端的通信。
a、注册一个 Acceptor 事件处理器到 mainReactor 中Acceptor 事件处理器所关注的事件是 ACCEPT 事件这样 mainReactor 会监听客户端向服务器端发起的连接请求事件(ACCEPT 事件)。启动 mainReactor 的事件循环。 b、 客户端向服务器端发起一个连接请求mainReactor 监听到了该 ACCEPT 事件并将ACCEPT 事件派发给 Acceptor 处理器来进行处理。Acceptor 处理器通过 accept()方法得到与这个客户端对应的连接(SocketChannel)然后将这个 SocketChannel 传递给 subReactor 线程池。 c、 subReactor 线程池分配一个 subReactor 线程给这个 SocketChannel即将SocketChannel 关注的 READ 事件以及对应的 READ 事件处理器注册到 subReactor 线程中。当然你也注册 WRITE 事件以及 WRITE 事件处理器到 subReactor 线程中以完成 I/O 写操作。Reactor 线程池中的每一 Reactor 线程都会有自己的 Selector、线程和分发的循环逻辑。 d、 当有 I/O 事件就绪时相关的 subReactor 就将事件派发给响应的处理器处理。注意这里 subReactor 线程只负责完成 I/O 的 read()操作在读取到数据后将业务逻辑的处理放入到线程池中完成若完成业务逻辑后需要返回数据给客户端则相关的 I/O 的 write 操作还是会被提交回 subReactor 线程来完成。 四、零拷贝 1、什么是零拷贝 零拷贝是指计算机执行操作是cpu不需要先将数据复制到另一个特定区域。为了网络传输文件时节省cpu周期和内存宽带 零拷贝技术可以减少数据拷贝和共享总线操作的次数消除传输数据再存储器之间的不必要的中间拷贝次数从而提高数据传输效率 零拷贝技术减少了用户进程地址空间和内核地址空间之间因为上下文切换带来的开销 所以零拷贝只是说减少荣誉的拷贝 下面这些组件、框架中均使用了零拷贝技术Kafka、Netty、Rocketmq、Nginx、Apache。 2、linux的I/O机制与DMA 早起用户进程读取磁盘数据需要CPU中断和CPU参与每次中断都会带来CPU的上下文切换 DMADiect Memory Access直接内存存取他允许不同速度的硬件装置来沟通不需要以来CPU的大量中断负载 DMA控制器接管了数据读写请求减少CPU负担现代硬盘基本都支持DMA 现在的IO读取的过程 a、DMA等待数据准备好把磁盘数据读取到操作系统的内核缓冲区 b、用户进程将内存缓冲区的数据copy到用户空间 传统数据传统过程 a、将磁盘文件读取到操作系统内核缓冲区 b、将内核缓冲区的数据copy到应用程序的buffer c、应用程序将buffer中的数据copy到socket网络发送缓冲区 d、将socket buffer的数据copy到网卡再有网卡进行网络传输 上述四次copy中第二次和第三次没有什么帮助反而浪费了系统资源
3、linux中支持的零拷贝 mmap内存映射 硬盘上文件的位置和应用程序缓冲区进行映射由于mmap将文件直接映射到用户空间所以实际文件读取根据这个映射直接将文件从硬盘copy到用户空间减少数据copy到内核缓冲空间
mmap 内存映射将会经历3 次拷贝: 1 次 cpu copy2 次 DMA copy 以及 4 次上下文切换调用 mmap 函数 2 次write 函数 2 次。 sendfile linux2.1支持的sendfile 当调用sendfile时DMA将磁盘数据复制到kernel buffer然后将数据直接copy到socket buffer数据并未真正复制到socket的buffer只将数据的位置和长度等描述复制到socket buffer中DMA将数据从内核缓冲区中直接传递给协议引擎消除遗留的最后一次复制这个需要DMA设备支持才行 sendfile 会经历32如果硬件设备支持次拷贝10如果硬件设备支持次 CPU copy 2 次 DMA copy以及 2 次上下文切换
splice linux从2.6.17支持splice 数据从磁盘读取到OS内核缓冲区后在内核缓冲区直接转成内核空间其它数据bufffer从而不需要copy到用户空间
和 sendfile()不同的是splice()不需要硬件支持。
splice 会经历 2 次拷贝: 0 次 cpu copy 2 次 DMA copy以及 2 次上下文切换 所以最终linux中的零拷贝是DMAcopy了一次cpu的零拷贝
4、java中的零拷贝 java中的仅支持两种mmapsendfile
NIO提供的内存映射MappedByteBuffer NIO 中的 FileChannel.map()方法其实就是采用了操作系统中的内存映射方式底层就是调用 Linux mmap()实现的。将内核缓冲区的内存和用户缓冲区的内存做了一个地址映射。这种方式适合读取大文件同时也能对文件内容进行更改但是如果其后要通过 SocketChannel 发送还是需要 CPU 进行数据的拷贝。
NIO提供的sendfile NIO 中的 FileChannel.map()方法其实就是采用了操作系统中的内存映射方式底层就是调用 Linux mmap()实现的。将内核缓冲区的内存和用户缓冲区的内存做了一个地址映射。这种方式适合读取大文件同时也能对文件内容进行更改但是如果其后要通过 SocketChannel 发送还是需要 CPU 进行数据的拷贝。