Simple understanding of IO and NIO

Posted by hnxuying on Mon, 03 Jan 2022 03:34:23 +0100

Main references: NIO learning documentation This great God writes very well and is easy to understand!!!!!!!!

1, NIO and IO contrast memory

IONIO
Flow orientedCache oriented
Blocking IOnon-blocking IO
selector

Stream oriented and cache oriented

  • Flow oriented
    Java IO is stream oriented, which means you read one or more bytes from a stream at a time. How to handle the bytes read is up to you. They are not cached anywhere. In addition, operations cannot move back and forth in the data flow. If you need to move back and forth in the data read from the stream, you need to cache it into the buffer first.
  • Buffer oriented
    The buffer oriented approach of Java NIO is slightly different. The data is read into a buffer and then processed from this buffer. You can move back and forth in the buffer according to the operation. This provides more flexibility in processing.

Blocking and non blocking

  • Java IO is blocked. A connection corresponds to one thread. When reading and writing a stream, the thread will block until the reading and writing is completed
  • Java NIO is non blocking. When a thread reads data from a channel, if there is no available data at this time, the thread will not be blocked and can continue to be used by other resources until there is available data to be read. The same is true for non blocking writes.

selector

Manage multiple channels using a single threaded control selector.

2, NIO

Three main concepts

passageway

Comparison of channel and flow

  • Channels can be read or written, and streams are generally unidirectional (they can only be read or written, so we need to create an input stream and an output stream respectively when using streams for IO operations).
  • The channel can read and write asynchronously.
  • The channel is always read and written based on the Buffer.

Implementation of channel

  • FileChannel: used for data reading and writing of files
  • Datagram channel: used for data reading and writing of UDP
  • SocketChannel: used for TCP data reading and writing. It is generally implemented by the client
  • ServerSocketChannel: allows us to listen to TCP link requests. Each request will create a SocketChannel, which is generally implemented by the server

    Java NIO: read data from Channels to Buffers, and write data from Buffers to Channels

cache

  • capacity
  • position
  • limit

selector

The Selector is a component that checks one or more Java NIO channel instances and determines which channels are ready for read or write operations
Using a selector allows a thread to process multiple channels, reducing the resource consumption of thread switching and thread management

Java NIO: a thread uses a selector to handle three channels

3, Non blocking server for NIO

The non blocking server needs to regularly check whether the incoming data and check the integrity of the data. The server may need to check multiple times until one or more complete messages are received, so it is a regular query.

Similarly, the non blocking server needs to regularly check whether there is data to write. If yes, the server needs to check the integrity of the written data to avoid partial writing of the data.

Therefore, the three inspectors that the server's channel needs to execute regularly are:

  • Read pipeline, incoming of new connection data;
  • Check data integrity;
  • Write the pipeline to check whether it can write any outgoing messages to any open connection.

4, RandomAccessFile class: dynamically read file contents

Tiktok: upload Internet files to jitter or fast hand, etc. when the file is too large, there may be Internet problem such as connection timeout when uploading. Channel officials generally recommend the use of the Kwai uploading mechanism to reduce the failure frequency of large file uploading. Use the RandomAccessFile of IO to complete the requirements. Similar to a NIO file operation, the http request is similar to a file upload channel.

RandomAccessFile supports reading and writing random access files. Meanwhile, RandomAccessFile supports "random access". The limitation is that it can only read and write files.

An important usage scenario of RandomAccessFile is multi-threaded download and breakpoint continuation in network requests. When the upload of a slice fails in the slice upload, the file fragment that needs to be re operated can be obtained according to the slice number and the position of the file displacement for re upload. I use the @ Retryable annotation to use the slice upload method.

@Retryable(maxAttempts = 5, backoff = @Backoff(value = 1000, multiplier = 1.5))

Four access modes:

  • "r"
    read-only
  • "rw"
    Readable and writable. If the file does not exist, an attempt is made to create it.
  • "rws"
    Readable and writable. Like "rw", it also requires that each update of file content or metadata be synchronously written to the underlying storage device.
  • "rwd"
    It is readable and writable. Like "rw", it also requires that each update of file content be synchronously written to the underlying storage device.

Code example:

		// read only mode
		RandomAccessFile raf = new RandomAccessFile(file, "r");
        try {
            long length = file.length();
            //The slice number starts from 1
            int startOffset = 1;
            int byteCount;
            // The cache takes the small values of the file length and the size of each slice as the capacity of the cache
            byte[] buff = new byte[(int) Math.min(SIZE, length)];
            boolean finish = false;
            while (!finish) {
                // The pointer position starts from 0, and each displacement = 1 * the size of each piece
                raf.seek((startOffset - 1) * SIZE);
                // Bytes per slice
                byteCount = raf.read(buff);
                // When the number of bytes uploaded this time does not reach the size of the partition required | the pointer has reached the end, which proves to be the last partition
                if (byteCount < SIZE || (startOffset) * SIZE == length) {
                    byte[] realChunkData = new byte[byteCount];
                    System.arraycopy(buff, 0, realChunkData, 0, byteCount);
                    buff = realChunkData;
                    finish = true;
                }
                //Slice upload nio: the Buffer buffer that stores data is transported through the Channel pipeline to realize data processing
                this.uploadVideoShard(startOffset, buff);
                // Modify pointer position not finished + 1 each time
                startOffset = finish ? startOffset : startOffset + 1;
                //After the split upload is completed, start the file splitting and merging
                if (finish) {
                    JSONObject result = restTemplate.postForObject(completeUrl, HttpEntity.EMPTY, JSONObject.class);
                }
            }
        } finally {
            if (file.exists()) {
                file.delete();
            }
            // Close file
            raf.close();
        }

Topics: Operation & Maintenance network server io