IO flow and system
IO technology is an extremely complex module in the JDK. One of the key reasons for its complexity is the correlation between IO operations and the system kernel. In addition, network programming and file management rely on IO technology, and they are difficult to program.
Linux space Isolation
Linux usage is user-specific. This is basic knowledge, and the underlying layer also distinguishes between user and kernel modules:
- User space: indicates the User space
- Kernel space: indicates the Kernel space
The permissions of common user space are much weaker than the permissions of kernel space operation, which involves the interaction between the user and the two modules of kernel. In this case, if the application deployed on the service needs to request system resources, the interaction will be more complicated:
The user space itself cannot directly issue scheduling instructions to the system, but must pass the kernel. Data operations in the kernel must be copied to the user space first. This isolation mechanism can effectively protect the security and stability of the system.
Parameter to see
You can run the Top command to dynamically view data analysis and resource usage of processes.
us
: Percentage of CPU occupied by user space;sy
: Percentage of CPU occupied by kernel space;id
: Percentage of CPU occupied by idle processes;wa
: CPU usage of I/O waiting.
Wa index is one of the core items of monitoring in large-scale document task flow.
IO Collaboration Process
Now look at the flow in Figure [1] above. When the application end initiates the IO operation request, the request flows along each node on the link, and there are two core concepts:
- Node interaction modes: synchronous and asynchronous;
- IO data manipulation: blocking and non-blocking;
Synchronous/asynchronous IO, blocking/non-blocking IO, see the details below.
Ii. IO model analysis
1. Synchronous blocking
The way the user thread interacts with the kernel, where the application request is processed by one thread and the Accept and read methods block until the entire action is complete:
In the common CS architecture, this is the basic process of an I/O operation. In the scenario of high concurrency, the client’s request response will have serious performance problems and occupy too many resources.
2. Synchronization is non-blocking
Optimized on the basis of synchronous blocking IO, the current thread does not wait for data to be ready until replication is complete:
The thread returns immediately after the request and polls until it gets the data before stopping the polling. The disadvantage of this pattern is also obvious. If the data is ready, the thread is told to complete the subsequent action, which saves a lot of intermediate interaction.
3. Asynchronous notification mode
In asynchronous mode, the blocking mechanism is completely abandoned and processes are interacted in segments, which is similar to the common third-party interconnection mode. When a local service requests a third-party service, if the request process takes a long time, it is executed asynchronously, and the third-party service calls back for the first time to confirm that the request can be executed. The second callback is to push the processing result. This idea can greatly improve performance and save resources when dealing with complex problems:
Asynchronous mode for performance improvement is huge, of course, its corresponding processing mechanism is also more complex, program iteration and optimization is endless, in NIO mode again IO flow mode optimization.
File class
1. Basic description
As an abstract representation of the path names of files and directories, the File class is used to obtain metadata information about disk files, such as File name, size, modification time, and permission judgment.
Note: File does not operate on the data content of the File. The content of the File is called data, and the information about the File itself is called metadata.
public class File01 {
public static void main(String[] args) throws Exception {
// 1. Read the specified file
File speFile = new File(IoParam.BASE_PATH+"fileio-03.text");if(! speFile.exists()){boolean creFlag = speFile.createNewFile() ;
System.out.println("Create:"+speFile.getName()+"; Result:"+creFlag);
}
// 2, read the specified position
File dirFile = new File(IoParam.BASE_PATH) ;
// Check whether it is a directory
boolean dirFlag = dirFile.isDirectory() ;
if (dirFlag){
File[] dirFiles = dirFile.listFiles() ;
printFileArr(dirFiles);
}
// delete the specified file
if (speFile.exists()){
boolean delFlag = speFile.delete() ;
System.out.println("Delete:"+speFile.getName()+"; Result:"+delFlag); }}private static void printFileArr (File[] fileArr){
if(fileArr ! =null && fileArr.length>0) {for(File file : fileArr) { printFileInfo(file) ; }}}private static void printFileInfo (File file) {
System.out.println("Name:"+file.getName());
System.out.println("Length:"+file.length());
System.out.println("Path:"+file.getPath());
System.out.println("Document judgment:"+file.isFile());
System.out.println("Directory judgment:"+file.isDirectory());
System.out.println("Last modified:"+newDate(file.lastModified())); System.out.println(); }}Copy the code
The above case uses the File class in the basic structure and common methods (read, judge, create, delete), JDK source in the continuous update iteration, through the constructor of the class, method, annotation to judge the basic function of the class, is the necessary ability as a developer.
There are two key information descriptions missing in the File class: type and encoding. If you often develop the requirements of the File module, you know that these are two extremely complex points, which are prone to problems. Here is how to deal with them from the perspective of actual development.
2. File business scenario
As shown in the figure, in a normal file flow task, there are three basic forms of conversion: file, stream and data:
Basic process description:
- Source file generation, push file center;
- Notify the service to use the node to obtain the file;
- Business nodes for logical processing;
An obvious problem is that no node can be adapted to all file processing strategies, such as type and encoding. Faced with problems in complex scenarios, rule constraints are commonly used to solve problems, that is, to deal with things within the convention rules.
In the above flow, the data subject description when the source file node informs the business node:
public class BizFile {
/** * File task batch number */
private String taskId ;
/** * whether to compress */
private Boolean zipFlag ;
/** * file address */
private String fileUrl ;
/** * File type */
private String fileType ;
/** * File encoding */
private String fileCode ;
/** * Service association: database */
private String bizDataBase ;
/** * Business association: data table */
private String bizTableName ;
}
Copy the code
Encapsulate the whole process as a task, namely: task batches, file information, business library table routing, etc. Of course, these information can also be directly marked in the file naming policy, and the processing method is similar:
/** * Reads information based on the convention policy */
public class File02 {
public static void main(String[] args) {
BizFile bizFile = new BizFile("IN001",Boolean.FALSE, IoParam.BASE_PATH,
"csv"."utf8"."model"."score");
bizFileInfo(bizFile) ;
/* * Service check */
File file = new File(bizFile.getFileUrl());
if(! file.getName().endsWith(bizFile.getFileType())){ System.out.println(file.getName()+": wrong description..."); }}private static void bizFileInfo (BizFile bizFile){
logInfo("The task ID",bizFile.getTaskId());
logInfo("Decompress or not",bizFile.getZipFlag());
logInfo("File Address",bizFile.getFileUrl());
logInfo("File Type",bizFile.getFileType());
logInfo("File code",bizFile.getFileCode());
logInfo("Business base",bizFile.getBizDataBase());
logInfo("Business sheet",bizFile.getBizTableName()); }}Copy the code
The information based on the body description can also be translated into the following naming rules: naming policy: number _ compression _Excel_ code _ library _ table. In this way, files that do not comply with the convention are directly excluded during service processing, reducing data problems caused by file exceptions.
4. Basic flow mode
1. Overall overview
IO flow
Basic encoding logic: Source file -> Input stream -> Logical processing -> Output stream -> target file;
Depending on the perspective, flows can be divided into many modes:
- Flow direction: input flow, output flow;
- Stream data types: byte stream, character stream;
IO flow mode has many kinds, the corresponding API design is also very complex, usually complex API to grasp the core interface and common implementation classes and principles.
Based on the API
-
Byte stream: InputStream input, OutputStream output; The basic unit of data transmission is the byte;
- Read () : the next byte in the input stream to read data;
- Read (byte b[]) : read data cached into a byte array;
- Write (int b) : specifies bytes to write to the output stream;
- Write (byte b[]) : array bytes write to the output stream;
-
Character streams: Reader reads, Writer writes. The basic unit of data transmission is the character;
- Read () : reads a single character;
- Read (char cbuf[]) : reads the character array;
- Write (int c) : write a specified character;
- Write (char cbuf[]) : write an array of characters;
Buffer mode
The normal read/write mode of the IO stream is that the data is read and written out, and the other buffer mode is that the data is loaded into the buffer array, and the buffer should be filled again when the data is read:
Buffering mode has obvious advantages. It ensures efficient reading and writing and performs in isolation from the data filling process. BufferedInputStream, BufferedReader class is the concrete implementation of buffering logic.
2. Byte stream
API diagram:
Byte stream base API:
public class IoByte01 {
public static void main(String[] args) throws Exception {
// Source file destination file
File source = new File(IoParam.BASE_PATH+"fileio-01.png"); File target =new File(IoParam.BASE_PATH+"copy-"+source.getName()) ;
// Input stream output stream
InputStream inStream = new FileInputStream(source) ;
OutputStream outStream = new FileOutputStream(target) ;
// read and write
byte[] byteArr = new byte[1024];
int readSign ;
while((readSign=inStream.read(byteArr)) ! = -1){
outStream.write(byteArr);
}
// Close the input and output streamsoutStream.close(); inStream.close(); }}Copy the code
Byte stream buffer API:
public class IoByte02 {
public static void main(String[] args) throws Exception {
// Source file destination file
File source = new File(IoParam.BASE_PATH+"fileio-02.png"); File target =new File(IoParam.BASE_PATH+"backup-"+source.getName()) ;
// Buffering: input stream output stream
InputStream bufInStream = new BufferedInputStream(new FileInputStream(source));
OutputStream bufOutStream = new BufferedOutputStream(new FileOutputStream(target));
// read and write
int readSign ;
while((readSign=bufInStream.read()) ! = -1){
bufOutStream.write(readSign);
}
// Close the input and output streamsbufOutStream.close(); bufInStream.close(); }}Copy the code
Byte stream application scenario: data is the file itself, such as pictures, video, audio, etc.
3. Character stream
API diagram:
Character stream basic API:
public class IoChar01 {
public static void main(String[] args) throws Exception {
// Read text write text
File readerFile = new File(IoParam.BASE_PATH+"io-text.txt"); File writerFile =new File(IoParam.BASE_PATH+"copy-"+readerFile.getName()) ;
// character input/output stream
Reader reader = new FileReader(readerFile) ;
Writer writer = new FileWriter(writerFile) ;
// characters read in and write out
int readSign ;
while((readSign = reader.read()) ! = -1){
writer.write(readSign);
}
writer.flush();
/ / close the flowwriter.close(); reader.close(); }}Copy the code
Character stream buffering API:
public class IoChar02 {
public static void main(String[] args) throws Exception {
// Read text write text
File readerFile = new File(IoParam.BASE_PATH+"io-text.txt"); File writerFile =new File(IoParam.BASE_PATH+"line-"+readerFile.getName()) ;
// Buffer character input and output streams
BufferedReader bufReader = new BufferedReader(new FileReader(readerFile)) ;
BufferedWriter bufWriter = new BufferedWriter(new FileWriter(writerFile)) ;
// characters read in and write out
String line;
while((line = bufReader.readLine()) ! =null){
bufWriter.write(line);
bufWriter.newLine();
}
bufWriter.flush();
/ / close the flowbufWriter.close(); bufReader.close(); }}Copy the code
Character stream application scenarios: Files are used as data carriers, such as Excel, CSV, and TXT files.
4. Coding and decoding
- Encoding: character to byte;
- Decoding: converting bytes into characters;
public class EnDeCode {
public static void main(String[] args) throws Exception {
String var = "IO stream" ;
/ / code
byte[] enVar = var.getBytes(StandardCharsets.UTF_8) ;
for (byte encode:enVar){
System.out.println(encode);
}
/ / decoding
String deVar = new String(enVar,StandardCharsets.UTF_8) ;
System.out.println(deVar);
/ / the code
String messyVar = newString(enVar,StandardCharsets.ISO_8859_1) ; System.out.println(messyVar); }}Copy the code
The root cause of the occurrence of garbled code is the different coding types used in the two stages of encoding and decoding.
5. Serialization
- Serialization: The process of converting an object into a stream;
- Deserialization: The process of converting a stream into an object;
public class SerEntity implements Serializable {
private Integer id ;
private String name ;
}
public class Seriali01 {
public static void main(String[] args) throws Exception {
// serialize objects
OutputStream outStream = new FileOutputStream("SerEntity.txt"); ObjectOutputStream objOutStream =new ObjectOutputStream(outStream);
objOutStream.writeObject(new SerEntity(1."Cicada"));
objOutStream.close();
// Deserialize the object
InputStream inStream = new FileInputStream("SerEntity.txt");
ObjectInputStream objInStream = newObjectInputStream(inStream) ; SerEntity serEntity = (SerEntity) objInStream.readObject(); System.out.println(serEntity); inStream.close(); }}Copy the code
Note: Member objects of reference types must also be serializable, otherwise NotSerializableException will be thrown.
NIO mode
1. Basic concepts
NIO (NonBlockingIO), a data block-oriented processing mechanism, synchronous non-blocking model, a single thread on the server can process multiple client requests, the processing speed of IO stream has a very high improvement, three core components:
- Buffer: The underlying maintenance array stores data;
- Channel: supports read and write bidirectional operations.
- Selector: provides Channel multiple registration and polling capabilities;
API Use Cases
public class IoNew01 {
public static void main(String[] args) throws Exception {
// Source file destination file
File source = new File(IoParam.BASE_PATH+"fileio-02.png"); File target =new File(IoParam.BASE_PATH+"channel-"+source.getName()) ;
// Enter the byte channel
FileInputStream inStream = new FileInputStream(source);
FileChannel inChannel = inStream.getChannel();
// Outputs the byte channel
FileOutputStream outStream = new FileOutputStream(target);
FileChannel outChannel = outStream.getChannel();
// Direct channel replication
// outChannel.transferFrom(inChannel, 0, inChannel.size());
// Buffer read/write mechanism
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
while (true) {
// Read the data in the channel to the buffer
int in = inChannel.read(buffer);
if (in == -1) {
break;
}
// Read/write switch
buffer.flip();
// Write out buffer data
outChannel.write(buffer);
// Clear the bufferbuffer.clear(); } outChannel.close(); inChannel.close(); }}Copy the code
The above cases are only the most basic NIO file replication capability, and NIO mode has a very broad space to play in network communication.
2. Network communication
A single thread on the server can process multiple client requests and poll the multiplexer to see if there are ANY I/O requests. In this way, the concurrency capability of the server is greatly improved and the resource consumption is significantly reduced.
API example: server-side emulation
public class SecServer {
public static void main(String[] args) {
try {
// Start the service
ServerSocketChannel socketChannel = ServerSocketChannel.open();
socketChannel.socket().bind(new InetSocketAddress("127.0.0.1".8089));
// Set non-blocking to accept clients
socketChannel.configureBlocking(false);
// Turn on the multiplexer
Selector selector = Selector.open();
// The server Socket registers with the multiplexer and specifies the event of interest
socketChannel.register(selector, SelectionKey.OP_ACCEPT);
// Multiplexer polling
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
while (selector.select() > 0){
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> selectionKeyIter = selectionKeys.iterator();
while (selectionKeyIter.hasNext()){
SelectionKey selectionKey = selectionKeyIter.next() ;
selectionKeyIter.remove();
if(selectionKey.isAcceptable()) {
// Accept new connections
SocketChannel client = socketChannel.accept();
// Set read non-blocking
client.configureBlocking(false);
// Register with the multiplexer
client.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
// The channel is readable
SocketChannel client = (SocketChannel) selectionKey.channel();
int len = client.read(buffer);
if (len > 0){
buffer.flip();
byte[] readArr = new byte[buffer.limit()];
buffer.get(readArr);
System.out.println(client.socket().getPort() + "Port data :" + new String(readArr));
buffer.clear();
}
}
}
}
} catch(Exception e) { e.printStackTrace(); }}}Copy the code
API example: Client emulation
public class SecClient {
public static void main(String[] args) {
try {
// Connect to the server
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1".8089));
ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
String conVar = "[hello-8089]";
writeBuffer.put(conVar.getBytes());
writeBuffer.flip();
// Send data every 5S
while (true) {
Thread.sleep(5000); writeBuffer.rewind(); socketChannel.write(writeBuffer); writeBuffer.clear(); }}catch(Exception e) { e.printStackTrace(); }}}Copy the code
SelectionKey binds the association between Selector and Chanel, and fetches a collection of channels in the ready state.
Six, source code address
Making address GitEE, https://github.com/cicadasmile/java-base-parent, https://gitee.com/cicadasmile/java-base-parentCopy the code