上篇文章介绍了堆外内存DirectByteBuffer,我们知道了DirectByteBuffer是分配在JVM堆外的ByteBuffer,这篇文章来了解堆内内存HeapByteBuffer。HeapByteBufferHeapByteBuffer,即分配在JVM中的heap堆中的ByteBuffer,调用ByteBuffer#allocate()即可生成一个HeapByteBuffer对象。publicstaticByteBufferallocate(intcapacity){if(capacity<0)thrownewIllegalArgumentException();return
前面9篇文章我们已经深入了解了NIO的基本概念和核心原理,想必小伙伴们对NIO的三大组件已经了然于心了,对于ByteBuffer而言,其实还有两个较为特殊的类DirectByteBuffer和MappedByteBuffer没有分析,这两个类的原理都是基于内存文件映射的。ByteBuffer分为两种,一种是直接的,另外一种是间接的。直接缓冲:直接使用内存映射,对于Java而言就是直接在JVM之外分配虚拟内存地址空间,Java中使用DirectByteBuffer来实现,也就是堆外内存。间接缓冲:是在JVM堆上实现,Java中使用HeapByteBuffer来实现,也就是堆内内存。我们这篇文章主
多线程架构前面所有文章的示例服务端都是单线程模式,这种模式存在很多的缺陷无法充分利用多核CPU的性能如果服务端某个读写操作耗时较多,则会拖慢整个服务端所以,这篇文章大明哥将介绍服务端多线程的模式,让我们榨干服务器性能。我们清楚服务端主要做两件事,建立连接和处理读写事件,所以我们可以将服务端的线程分为两组:一个线程专门处理accept事件,我们称之为Boss线程CPU核心数个线程,这些线程处理读写事件,我们称之为Worker线程这个时候,客户端服务端的关系如下:Boss线程只服务处理Acept事件,Worker线程只处理读写事件,他们都各自维护一个Selector,每个Selector负责监听自
背景我们先看下面一段代码。publicstaticvoidmain(String[]args)throwsException{ServerSocketChannelserverSocketChannel=ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);serverSocketChannel.bind(newInetSocketAddress(8081));Selectorselector=Selector.open();serverSocketChannel.register(selector,Se
上篇文章(【死磕NIO】—详解Selector)详细介绍了Selector的核心原理和使用方法,这篇文章我们来深入了解Selector的源码,主要讲三个最常用的方法open(),register()和selector()。open()调用Selector.open()方法会创建一个Selector实例。publicstaticSelectoropen()throwsIOException{returnSelectorProvider.provider().openSelector();}SelectorProvider是一个抽象类,它提供了创建Selector、ServerSocketChan
前面4篇文章深入分析了NIO三大组件中的两个:Buffer和Channel:【死磕NIO】—深入分析Buffer【死磕NIO】—深入分析Channel和FileChannel【死磕NIO】—跨进程文件锁:FileLock【死磕NIO】—探索SocketChannel的核心原理这篇文章则介绍第三个组件:Selector。相比Buffer和Channel而言,Selector对于NIO来说显得更加重要,因为它是NIO实现多路复用的核心,它的使命就是完成IO的多路复用。Selector简介在前一篇文章:【死磕NIO】—ServerSocketChannel的应用实例,大明哥分析了ServerSock
[TOC]一、传统IO问题传统的IO将一个文件通过socket写出Filef=newFile("helloword/data.txt");RandomAccessFilefile=newRandomAccessFile(file,"r");byte[]buf=newbyte[(int)f.length()];file.read(buf);Socketsocket=...;socket.getOutputStream().write(buf);内部工作流程是这样的:java本身并不具备IO读写能力,因此read方法调用后,要从java程序的用户态切换至内核
[TOC]一、IO流程就是对于Linux系统,I/O操作不是一步完成的。此处的I/O操作是一个通用型的概念,对于socket通信,也可以看作一个I/O操作过程,只不过操作的是网络对象。I/O操作一般分为两个部分:应用程序发起I/O操作请求,等待数据,或者将要操作的数据拷贝到系统内核中(比如socket)。系统内核进行I/O操作(一般是内核将数据拷贝到用户进程中)。阻塞和非阻塞首先明确一点:阻塞和非阻塞发生在请求处,关注的是程序在等待调用结果时的状态。通过上面的概念可以很容易的理解以下结论:阻塞调用是指调用结果返回之前,当前进程(线程)会被挂起。调用进程(线程)阻塞在I/O操作请求处,直到I/O
[TOC]前言之前说到的服务端程序都是在一个线程上进行的,这个线程不仅负责连接客户端发来的请求,同时还要处理读写事件,这样效率还是不够高。如今电脑都是多核处理器,这意味着可以同时进行多个线程,所以服务端应该充分利用这一点。一、概述服务端线程可以建立多个线程,将这些线程分成两组:单线程配一个选择器(Boss),专门处理accept事件创建cpu核心数的线程(Worker),每个线程配一个选择器,轮流处理read事件关系图说明Boss线程只负责Accept事件,Worker线程负责客户端与服务端之间的读写问题,他们都各自维护一个Selector负责监听通道的事件。当Boss线程检测到有客户端的连接
一次无法写完例子非阻塞模式下,无法保证把buffer中所有数据都写入channel,因此需要追踪write方法的返回值(代表实际写入字节数)用selector监听所有channel的可写事件,每个channel都需要一个key来跟踪buffer,但这样又会导致占用内存过多,就有两阶段策略当消息处理器第一次写入消息时,才将channel注册到selector上selector检查channel上的可写事件,如果所有的数据写完了,就取消channel的注册如果不取消,会每次可写均会触发write事件服务端代码importjava.io.IOException;importjava.net.Inet
[TOC]一、消息边界问题的产生1.1服务端代码importlombok.extern.slf4j.Slf4j;importjava.io.IOException;importjava.net.InetSocketAddress;importjava.nio.ByteBuffer;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SocketChannel;importj
[TOC]前言多路复用单线程可以配合Selector完成对多个Channel可读写事件的监控,这称之为多路复用多路复用仅针对网络IO、普通文件IO没法利用多路复用如果不用Selector的非阻塞模式,线程大部分时间都在做无用功,而Selector能够保证有可连接事件时才去连接有可读事件才去读取有可写事件才去写入限于网络传输能力,Channel未必时时可写,一旦Channel可写,会触发Selector的可写事件一、处理accept事件服务器端代码importlombok.extern.slf4j.Slf4j;importjava.io.IOException;importjava.net.In
[TOC]一、概述Selector一般称为选择器,也可以翻译为多路复用器,是JavaNIO核心组件之一,主要功能是用于检查一个或者多个NIOChannel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个Channel(通道),当然也可以管理多个网络连接。使用Selector的好处在于,可以使用更少的线程来处理更多的通道,相比使用更多的线程,避免了线程上下文切换带来的开销等。二、Selector(选择器)方法2.1Selector的创建通过调用静态工厂方法Selector.open()方法创建一个Selector对象。Selectorselector=Selector.open();
[TOC]前言SocketChannel方法介绍创建一个服务器对象ServerSocketChannel.open()*服务器对象需要绑定ip和端口,使用`bind(InetSocketAddress)`方法,需要使用传入`InetSocketAddress`,只需传入一个端口号即可;*服务器调用accept()方法获取客户端的连接请求;*通过接收到的客户端连接对象`read(buffer)`方法获取客户端发送的消息。创建客户端SocketChannel.open()*客户端使用`connect(InetSocketAddressserver)`方法,连接对应的服务器;*通过`write(bu
[TOC]一、遍历目录文件importjava.io.IOException;importjava.nio.file.*;importjava.nio.file.attribute.BasicFileAttributes;importjava.util.concurrent.atomic.AtomicInteger;/***@authorlilinchao*@date2022/5/30*@description遍历目录,统计文件和文件夹个数**/publicclassFilesTraverseDirectoryDemo{publicstaticvoidmain(String[]args)thr
[TOC]一、概述Files是Java1.7在nio中新增的专门用于处理文件和目录的工具类。Files和Path配合可以很方便的完成对文件/目录的创建、读取、修改、删除等操作。二、常用方法介绍2.1Files.exits()booleanexists(Pathpath,LinkOption...options)描述:方法检查一个路径是否存在于当前的文件系统中。参数:Path:传入的文件路径。必须LinkOption:exits()方法的选项数组。如LinkOption.NOFOLLOW_LINKS代表不允许跟随文件系统中的符号链接来确定路径是否存在。非必须Pathpath=Paths.get(
[TOC]一、Path介绍Path接口是javaNIO2的一部分。首次在java7中引入。Path接口在java.nio.file包下,所以全称是java.nio.file.Path。java中的Path表示文件系统的路径。可以指向文件或文件夹。Path同时也有相对路径和绝对路径之分:绝对路径:表示从文件系统的根路径到文件或是文件夹的路径。相对路径:表示从特定路径下访问指定文件或文件夹的路径。二、Path方法介绍2.1创建Path实例如果使用java.nio.file.Path实例,必须先创建它。通过Paths.get(Stringfirst,String…more)静态工厂方法创建。impo
将数据写入指定文件importlombok.extern.slf4j.Slf4j;importjava.io.File;importjava.io.IOException;importjava.nio.ByteBuffer;importjava.nio.channels.FileChannel;importjava.nio.charset.StandardCharsets;importjava.nio.file.StandardOpenOption;/***Createdbylilinchao*Date2022/5/27*DescriptionchannelDemo*/@Slf4jpublic
[TOC]一、概念FileChannel是一个连接到文件的通道,使用FileChannel可以从文件读数据,也可以向文件中写入数据。JavaNIO的FileChannel是标准JavaIO读写文件的替代方案。FileChannel的主要作用是读取、写入、映射、操作文件。FileChannel只能工作在阻塞模式下。FileChannel和标准JavaIO对比FileInputStream/FileOutputStreamFileChannel单向双向面向字节的读写面向Buffer读写不支持支持内存文件映射不支持支持转入或转出其他通道不支持支持文件锁不支持操作文件元信息不支持操作文件元信息File
[TOC]一、示例网络上有多条数据发送给服务端,数据之间使用\n进行分隔>但由于某种原因这些数据在接收时,被进行了重新组合,例如原始数据有3条为Hello,world\nI'mzhangsan\nHowareyou?\n变成了下面的两个byteBuffer(黏包,半包)Hello,world\nI'mzhangsan\nHowareyou?\n分析本来分别将上方三条数据发送给服务端,但是发送到服务的却是两条第一条数据中的第二行和第三行中的Ho当作一条数据发送给了服务端,产生了黏包。第二条数据,因为服务端并未接收到完整的第三条数据,所以产生了半包。二、黏包和半包出现原因黏包发送方在发送数据
[TOC]1.分配内存空间可以使用allocate()和allocateDirect()方法为ByteBuffer分配空间,其他buffer类也有该方法allocate():使用的是java的堆内存,堆内字节缓冲区,读写效率低,会受到GC的影响allocateDirect():使用的是直接内存,直接内存字节缓冲区,读写效率高(零拷贝),不会受GC影响,因为是系统直接内存,所以分配内存要调用操作系统函数,所以分配内存的速度较慢,如果使用不当(资源没得到合理释放),会造成内存泄漏。importjava.nio.ByteBuffer;/***Createdbylilinchao*Date2022/5
[TOC]一、bytebuffer内部结构1.1属性介绍Bytebuffer有以下重要属性:capacity(容量):缓冲区的容量。通过构造函数赋予,一旦设置,无法更改。position(指针):读写指针,记录数据读写的位置,缓冲区的位置不能为负,并且不能大于limit。limit(读写限制):缓冲区的界限。位于limit后的数据不可读写。缓冲区的限制不能为负,并且不能大于其容量。1.2结构写模式当缓冲区刚创建成功时写模式下,position是写入位置,limit等于容量。下图表示写入了4个字节后的状态:Position移动到第5个字节开始位置读模式flip动作发生后,position切换为读
[TOC]前言本篇将通过nio读取一个文本文件来演示bytebuffer的基本使用一、准备数据准备创建data.txt文件,增加如下内容:1234567890abcd创建Maven项目安装lomback插件二、ByteBuffer使用分析向buffer写入数据,例如调用channel.read(buffer)调用flip()切换至读模式从buffer读取数据,例如调用buffer.get()调用clear()或compact()切换至写模式重复1~4步骤三、代码实现引入pom依赖<properties><maven.compiler.source>1.8</mav
[TOC]前言JavaNIO有两种解释:一种叫非阻塞IO(Non-blockingI/O)另一种叫新的IO(NewI/O)其实两种概念也是相同的。一、概述JavaNIO是从Java1.4版本开始引入的一个新的IOAPI,可以代替标准的IOAPI。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的,基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。NIO有三大核心部分Channel(通道)Buffer(缓冲区)Selector(选择器)二、JavaNIO与BIO的区别BIO以流的方式处理数据,而NIO以块的方式处理数据,块IO的效率比流IO高很多;
同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的。所以先限定一下本文的上下文。本文讨论的背景是Linux环境下的networkIO。一、概念说明在进行解释之前,首先要说明几个概念:进程切换为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。从一个进程的运行转到另一个进程上运行,这个过程中经过下面这些变化:保存处理机上下文,包括程序计数器和其他寄存器。更新PCB信息。把进程的PCB移入相应的队列,
一、I/O与CPU时间的比较I/O操作比在内存中进行数据处理任务所需时间更长,差别要以数量级计。许多程序员一门心思扑在他们的对象如何加工数据上,对影响数据读取和存储的环境问题却不屑一顾。表1-1所示为对数据单元进行磁盘读写所需时间的假设值。第一列为处理一个数据单元所需平均时间第二列为对该数据单元进行磁盘读写所需时间第三列为每秒所能处理的数据单元数第四列为改变第一第二列的值所能产生的数据吞吐率的提升值前三行显示了处理阶段的效率提升会如何影响吞吐率。把单位处理时间减半,仅能提高吞吐率2.2%。而另一方面,仅仅缩短I/O延迟10%,就可使吞吐率增加9.7%;把I/O时间减半,吞吐率几乎翻番。当您了解
一、Netty高性能之道传统RPC调用性能问题问题1网络传输方式问题传统的RPC框架或者基于RMI等方式的远程服务(过程)调用采用了同步阻塞IO,当客户端的并发压力或者网络时延增大之后,同步阻塞IO会由于频繁的wait导致IO线程经常性的阻塞,由于线程无法高效的工作,IO处理能力自然下降。下面,我们通过BIO通信模型图看下BIO通信的弊端:采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,接收到客户端连接之后为客户端连接创建一个新的线程处理请求消息,处理完成之后,返回应答消息给客户端,线程销毁,这就是典型的一请求一应答模型。该架构最大的问题就是不具备弹性伸缩
在讲netty之前我们现总结一下JAVANIO/JAVAAIO的不足之处。一、JAVANIO、AIO的不足之处虽然JAVANIO和JAVAAIO框架提供了多路复用IO/异步IO的支持,但是并没有提供上层“信息格式”的良好封装。例如前两者并没有提供针对ProtocolBuffer、JSON这些信息格式的封装,但是Netty框架提供了这些数据格式封装(基于责任链模式的编码和解码功能)要编写一个可靠的、易维护的、高性能的(注意它们的排序)NIO/AIO服务器应用。除了框架本身要兼容实现各类操作系统的实现外。更重要的是它应该还要处理很多上层特有服务,例如:客户端的权限、还有上面提到的信息格式封装、简单
一、异步IO模型异步IO则采用“订阅-通知”模式:即应用程序向操作系统注册IO监听,然后继续做自己的事情。当操作系统发生IO事件,并且准备好数据后,在主动通知应用程序,触发相应的函数和同步IO一样,异步IO也是由操作系统进行支持的。微软的windows系统提供了一种异步IO技术:IOCP(I/OCompletionPort,I/O完成端口);Linux下由于没有这种异步IO技术,所以使用的是epoll(上文介绍过的一种多路复用IO技术的实现)对异步IO进行模拟。二、JAVAAIO框架简析JAVAAIO框架在windows下使用windowsIOCP技术,在Linux下使用epoll多路复用IO
一、多路复用IO模型场景描述一个餐厅同时有100位客人到店,当然到店后第一件要做的事情就是点菜。但是问题来了,餐厅老板为了节约人力成本目前只有一位大堂服务员拿着唯一的一本菜单等待客人进行服务。方法A:无论有多少客人等待点餐,服务员都把仅有的一份菜单递给其中一位客人,然后站在客人身旁等待这个客人完成点菜过程。在记录客人点菜内容后,把点菜记录交给后堂厨师。然后是第二位客人。。。。然后是第三位客人。很明显,只有脑袋被门夹过的老板,才会这样设置服务流程。因为随后的80位客人,再等待超时后就会离店(还会给差评)。方法B:老板马上新雇佣99名服务员,同时印制99本新的菜单。每一名服务员手持一本菜单负责一位