Introduction to Netty
1. Netty is an asynchronous, event driven network application framework to quickly develop high-performance and reliable network IO programs.
2. Netty is mainly aimed at high concurrency applications for Clients under TCP protocol, or applications for continuous transmission of a large amount of data in Peer-to-Peer scenario.
3. Netty is essentially a NIO framework, which is applicable to a variety of application scenarios related to server communication
Friendly tips:
Netty is implemented based on NIO, NIO is programmed based on java Native IO, and java Native IO is implemented based on TCP/IP
understand:
1. Asynchronous: similar to ajax
2. Event driven: a thread has a selector. When a client has event actions (actions include sending messages, connecting to the server, replying to messages, receiving messages, etc.), the selector will monitor and let the thread execute.
Friendly tip: non blocking, monitor the channel. If the channel does not act, it will monitor the next channel
I/O model
The simple understanding of I/O model is that what channel is used to send and receive data, which largely determines the performance of program communication
Java supports three network programming models / IO modes: BIO, NIO and AIO
Java BIO: synchronous and blocking (traditional blocking type). The server implementation mode is one thread per connection, that is, when the client has a connection request, the server needs to start a thread for processing. If the connection does not do anything, it will cause unnecessary thread overhead. Java BIO is traditional java io programming, and its related classes and interfaces are in java io.
[simple schematic diagram]
Java NIO: synchronous non blocking. The server implementation mode is that one thread processes multiple requests (connections), that is, the connection requests sent by the client will be registered on the multiplexer, and the multiplexer will process the I/O requests when it polls the connection.
Friendly note: for multiplexing, it is only for network transmission. Local file replication does not exist. The selector here is equivalent to the registry of microservices. Multiple clients need to register with the selector. And the selector monitors the events of each client.
[simple schematic diagram]
Java AIO(NIO.2): asynchronous and non blocking. AIO introduces the concept of asynchronous channel and adopts the Proactor mode, which simplifies the programming and starts the thread only after an effective request. Its feature is that the operating system notifies the server program to start the thread for processing after it is completed. It is generally used in applications with a large number of connections and a long time. (a brief introduction is not the key point. Remember that netty5. X is based on its implementation, but the effect is not very good, so it is invalidated because of the Linux system itself)
Analysis of applicable scenarios of BIO, NIO and AIO
Java BIO working mechanism
Java BIO application instance
Example description:
1) Write a server side using BIO model, listen to port 6666, and start a thread to communicate with it when there is a client connection.
2) It is required to improve the thread pool mechanism, which can connect multiple clients
3) The server can receive the data sent by the client (telnet).
The following is the code implementation:
Server:
package com.atguigu.bio; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class BIOServer { public static void main(String[] args) throws Exception { //Thread pool mechanism //thinking //1. Create a thread pool //2. If there is a client connection, create a thread to communicate with it (write a separate method) ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); //Create ServerSocket ServerSocket serverSocket = new ServerSocket(6666); System.out.println("The server started"); while (true) { System.out.println("Thread information id =" + Thread.currentThread().getId() + " name=" + Thread.currentThread().getName()); //Listen and wait for the client to connect System.out.println("Waiting for connection...."); // This place will be blocked. If there is no user connection, it will be blocked here final Socket socket = serverSocket.accept(); System.out.println("Connect to a client"); //Create a thread and communicate with it (write a separate method) newCachedThreadPool.execute(new Runnable() { public void run() { //We rewrite //Can communicate with clients handler(socket); } }); } } //Write a handler method to communicate with the client public static void handler(Socket socket) { try { System.out.println("Thread information id =" + Thread.currentThread().getId() + " name=" + Thread.currentThread().getName()); byte[] bytes = new byte[1024]; //Get input stream through socket InputStream inputStream = socket.getInputStream(); //Cyclic reading of data sent by the client while (true) { System.out.println("Thread information id =" + Thread.currentThread().getId() + " name=" + Thread.currentThread().getName()); System.out.println("read...."); // This place will be blocked. It will be blocked when the user has no input int read = inputStream.read(bytes); if(read != -1) { System.out.println(new String(bytes, 0, read )); //Output data sent by client } else { break; } } }catch (Exception e) { e.printStackTrace(); }finally { System.out.println("Close and client Connection of"); try { socket.close(); }catch (Exception e) { e.printStackTrace(); } } } }
Client:
package yu.learn; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class Client { public static void main(String[] args) { // Create a Socket through the constructor and connect the server with the specified address and port try { Socket socket = new Socket("127.0.0.1", 6666); System.out.println("Please enter information"); new ReadMsg(socket).start(); PrintWriter pw = null; // Write data to the server while (true) { pw = new PrintWriter(socket.getOutputStream()); pw.println(new Scanner(System.in).next()); pw.flush(); } } catch (IOException e) { e.printStackTrace(); } } public static class ReadMsg extends Thread { Socket socket; public ReadMsg(Socket socket) { this.socket = socket; } @Override public void run() { try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { String line = null; // Read the data transmitted by the server through the input stream while ((line = br.readLine()) != null) { System.out.printf("%s\n", line); } } catch (IOException e) { e.printStackTrace(); } } } }
Java BIO problem analysis
Basic introduction to Java NIO (key points)
1. NIO has three core parts: channel, buffer and selector
2. NIO is buffer oriented or block oriented programming.
Friendly tip: why is BIO not blocked compared to the buffer zone? It's just like people holding a pile of cans. It's more troublesome for the people in front to throw the cans to you little by little. You just say to let the people in front directly throw the cans into the bag. I'll take them away at one time. When he packs, you can listen to music and drink water.
NIO basic introduction
Comparison of NIO and BIO
Friendly tip: BIO is through byte stream and character stream. It is one-way. NIO is through Channel and Buffer, and Channel is bidirectional. The NIO is bidirectional.
Relationship between three core components of NIO
Friendly note: in fact, both the client and the server need a buffer when connecting to the Channel. The following figure is incomplete. It should be noted that the client and the server directly interact with each other through the buffer, and then interact with the Channel through the buffer
Buffer core component I
Basic introduction
Friendly note: buffer has some internal mechanisms that can track and record the state changes of the buffer. Channel is the channel. It can be imagined that the binary stream transmitted by the network is people. Channel is a way to reach the destination, such as waterway, air flight and road (in the network, it is equivalent to protocols such as WS protocol and http protocol). This buffer is vehicles, ships, aircraft and cars.
Buffer class and its children
ByteBuffer structure
ByteBuffer has the following four important properties
- capacity(): it is equivalent to the initial capacity given by the array and remains unchanged
- position(): it is equivalent to a pointer. It moves continuously. The initial value is 0
- limit(): the limit position where the pointer moves, that is, the final position of the written data.
- mark(): indicates a mark. If a mark is added to a certain position, it can be specified at the specified position and continuously read data from the mark bit. The initial value is - 1
be careful:
-
mark() and reset()
mark() makes a mark when reading. Even if the position changes, you can return to the position of mark as long as you call reset
Rewind and flip will clear the mark position. You can call the rewind method to reset the position to 0
Friendly note: after Clear execution, the position will be written to the starting position again, but after compact execution, the unread data will be compressed to the starting position.
Initial state
In write mode
Switching using flip
After reading 4 bytes
The clear action will program the initial write state (from read to write)
The Compact method will move the unread data to the starting position. (from read to write)
Friendly tip: the last bit of mobile data is still the most original data, but the original data will be overwritten when writing.
Buffer related methods
ByteBuffer common methods
Friendly tips:
When the objects of these two methods are output, they are found
System.out.println(ByteBuffer.allocate(16).getClass());For heap System.out.println(ByteBuffer.allocateDirect(16).getClass());For direct memory
class java.nio.HeapByteBuffer - java Heap memory, with low read and write efficiency, is affected GC Influence of class java.nio.DirectByteBuffer - Direct memory, high reading and writing efficiency (less than one copy), and will not be affected GC Low efficiency of distribution
code:
package com.atguigu.nio; import java.nio.IntBuffer; public class BasicBuffer { public static void main(String[] args) { //Give an example to illustrate the use of Buffer (simple description) //Create a Buffer with a size of 5, which can store 5 int s IntBuffer intBuffer = IntBuffer.allocate(5); //Store data to buffer // intBuffer.put(10); // intBuffer.put(11); // intBuffer.put(12); // intBuffer.put(13); // intBuffer.put(14); for(int i = 0; i < intBuffer.capacity(); i++) { intBuffer.put( i * 2); } //How to read data from buffer //buffer conversion, read / write switching (!!!) /* public final Buffer flip() { limit = position; //Read data cannot exceed 5 position = 0; mark = -1; return this; } */ intBuffer.flip(); intBuffer.position(1);//1,2 System.out.println(intBuffer.get()); intBuffer.limit(3); while (intBuffer.hasRemaining()) { System.out.println(intBuffer.get()); } } }
String to ByteBuffer
a key:
Convert string to ByteBuffer ByteBuffer byteBuffer2= StandardCharsets.UTF_8.encode("Me");
Buffer To convert to a string, you should note that it needs to be converted to a string Buffer conduct flip Otherwise, there is no display byteBuffer.flip(); String str= StandardCharsets.UTF_8.decode(byteBuffer).toString();
package yu.learn.FileChannel; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; public class StringAndBuffer { public static void main(String[] args) { // ByteBuffer byteBuffer=ByteBuffer.allocate(100); byteBuffer.put("You are?".getBytes()); // Convert string to ByteBuffer ByteBuffer byteBuffer2= StandardCharsets.UTF_8.encode("Me"); System.out.println(new String(byteBuffer2.array())); // When converting a Buffer to a string, it should be noted that the Buffer needs to be flip ped to convert to a string, otherwise it will not be displayed byteBuffer.flip(); String str= StandardCharsets.UTF_8.decode(byteBuffer).toString(); System.out.println(str); } }
Channel
Friendly tips:
1. The channel can read and write data asynchronously, and can be read and written
2. When using the channel, the read and write methods are for the channel. The read method reads data from the channel to the buffer, and the write method writes data from the buffer to the channel.
3. A Channel can be understood as a stream.
Common channels
- FileChannel: dedicated to reading files
- Datagram channel: dedicated to UDP
- SocketChannel: the client corresponding to network transmission, which is used for TCP transmission
- ServerSocketChannel: the server corresponding to network transmission, which is used for TCP transmission
The following are the corresponding different channels
Non blocking vs blocking
block
- In blocking mode, the related methods will cause the thread to pause
- ServerSocketChannel.accept suspends the thread when no connection is established
- SocketChannel.read pauses the thread when there is no data to read
- The performance of blocking is actually that the thread is suspended. During the suspension, the cpu will not be occupied, but the thread is equivalent to idle
- In a single thread, blocking methods interact with each other and can hardly work normally, requiring multithreading support
- However, under multithreading, there are new problems, which are reflected in the following aspects
- A 32-bit jvm has 320k threads and a 64 bit jvm has 1024k threads. If there are too many connections, it will inevitably lead to OOM and too many threads. On the contrary, the performance will be reduced due to frequent context switching
- Thread pool technology can be used to reduce the number of threads and thread context switching, but the symptoms are not the root cause. If many connections are established but inactive for a long time, all threads in the thread pool will be blocked. Therefore, it is not suitable for long connections, but only for short connections
Friendly tips:
1. The accept() and read() methods are blocked. For the NIO model, use configure blocking (false); Make it non blocking.
2. If single thread is used, blocking methods will affect each other, which is very inefficient and can hardly work.
3. If multithreading is used, the thread pool can be used to reduce the thread switching of online documents. However, if it is a long connection, the thread cannot release resources, and the thread pool is useless. Therefore, it is only suitable for short connection
Non blocking
- In non blocking mode, will the related methods suspend the thread
- In serversocketchannel When no connection is established, accept will return null and continue to run
- SocketChannel.read returns 0 when there is no data to read, but the thread does not need to be blocked. You can read other socketchannels or execute serversocketchannel accept
- When writing data, the thread just waits for the data to be written to the Channel without waiting for the Channel to send the data through the network
- However, in non blocking mode, even if there is no connection and readable data, the thread is still running, wasting cpu in vain
- During data replication, threads are actually blocked (where AIO improves)
Multiplexing
A single thread can cooperate with the Selector to monitor multiple Channel read-write events, which is called multiplexing
- Multiplexing is only for network IO and ordinary file IO, and multiplexing cannot be used
- Without the non blocking mode of the Selector, threads spend most of their time doing useless work, and the Selector can ensure that
- Connect only when there are connectable events
- Read only when there are readable events
- Write only when there are writable events
- Limited to the network transmission capacity, the Channel may not always be writable. Once the Channel is writable, the writable event of the Selector will be triggered
FileChannel class
As a common method of channel, FileChannel does not have a non blocking mode, so it cannot be used together with the Selector, and the Selector will appear in network programming
Note that transferTo uses the knowledge of zero copy
Example 1:
Example requirements:
1) Use the ByteBuffer and filechannel learned above to write "hello, Silicon Valley" to file01 Txt
2) Create if file does not exist
import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class FileChannelTest { public static void main(String[] args) throws IOException { // String str="Who am I?"; FileOutputStream fileOutputStream = null; try{ fileOutputStream=new FileOutputStream("d:\\file01.txt"); FileChannel fileChannel= fileOutputStream.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); byteBuffer.put(str.getBytes()); byteBuffer.flip(); fileChannel.write(byteBuffer); }catch (IOException e){ e.printStackTrace(); }finally{ fileOutputStream.close(); } } }
Example 2:
Example requirements:
1) Use the ByteBuffer and filechannel learned above to set file01 Txt is read into the program and displayed on the console screen
2) Assume that the file already exists
package com.atguigu.nio; import java.io.File; import java.io.FileInputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class NIOFileChannel02 { public static void main(String[] args) throws Exception { //Create an input stream for the file File file = new File("d:\\file01.txt"); FileInputStream fileInputStream = new FileInputStream(file); //Get the corresponding filechannel - > actual type FileChannelImpl through fileInputStream FileChannel fileChannel = fileInputStream.getChannel(); //Create buffer ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length()); //Read the data of the channel into the Buffer fileChannel.read(byteBuffer); //Convert byte data of byteBuffer to String System.out.println(new String(byteBuffer.array())); fileInputStream.close(); } }
In addition:
package cn.itcast.nio.c2; import lombok.extern.slf4j.Slf4j; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; @Slf4j public class TestByteBuffer { public static void main(String[] args) { // FileChannel // 1. Input / output stream, 2 RandomAccessFile try (FileChannel channel = new FileInputStream("data.txt").getChannel()) { // Prepare buffer ByteBuffer buffer = ByteBuffer.allocate(10); while(true) { // Read data from the channel and write data to the buffer int len = channel.read(buffer); log.debug("Bytes read {}", len); if(len == -1) { // There is no content break; } // Print the contents of the buffer buffer.flip(); // Switch to read mode while(buffer.hasRemaining()) { // Is there any unread data left byte b = buffer.get(); log.debug("Actual byte {}", (char) b); } buffer.clear(); // Switch to write mode } } catch (IOException e) { e.printStackTrace(); } } }
Example 3:
The idea of the following implementation is that both reading and transmission are through a buffer
code
package com.atguigu.nio; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class NIOFileChannel03 { public static void main(String[] args) throws Exception { FileInputStream fileInputStream = new FileInputStream("1.txt"); FileChannel fileChannel01 = fileInputStream.getChannel(); FileOutputStream fileOutputStream = new FileOutputStream("2.txt"); FileChannel fileChannel02 = fileOutputStream.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate(512); while (true) { //Cyclic reading //Here is an important operation. Don't forget /* public final Buffer clear() { position = 0; limit = capacity; mark = -1; return this; } */ byteBuffer.clear(); //Empty buffer int read = fileChannel01.read(byteBuffer); System.out.println("read =" + read); if(read == -1) { //Indicates that you have finished reading break; } //Write the data in the buffer to filechannel02 -- 2 txt byteBuffer.flip(); fileChannel02.write(byteBuffer); } //Close the associated flow fileInputStream.close(); fileOutputStream.close(); } }
Example 4
Friendly tip: pay attention to the use of transferFrom
The missing (output stream, write) calls the transferFrom method
transferFrom parameters: owner (input stream, read), position, read location, and length of owner resource
destCh.transferFrom(sourceCh,0,sourceCh.size());
Using the transferFrom method of copying files
Example requirements:
1) Use filechannel and method transferFrom to copy the file
2) Copy a picture
package com.atguigu.nio; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.channels.FileChannel; public class NIOFileChannel04 { public static void main(String[] args) throws Exception { //Create correlation flow FileInputStream fileInputStream = new FileInputStream("d:\\a.jpg"); FileOutputStream fileOutputStream = new FileOutputStream("d:\\a2.jpg"); //Get the filechannel corresponding to each stream FileChannel sourceCh = fileInputStream.getChannel(); FileChannel destCh = fileOutputStream.getChannel(); //Use transferForm to complete the copy destCh.transferFrom(sourceCh,0,sourceCh.size()); //Close relevant channels and streams sourceCh.close(); destCh.close(); fileInputStream.close(); fileOutputStream.close(); } }
Precautions and details about Buffer and Channel
1. What type is stored is what type is obtained. Otherwise, a BufferUnderflowException exception will be reported
What type to write and what type to take
package com.atguigu.nio; import java.nio.ByteBuffer; public class NIOByteBufferPutGet { public static void main(String[] args) { //Create a Buffer ByteBuffer buffer = ByteBuffer.allocate(64); //Type data buffer.putInt(100); buffer.putLong(9); buffer.putChar('still'); buffer.putShort((short) 4); //take out buffer.flip(); System.out.println(); System.out.println(buffer.getInt()); System.out.println(buffer.getLong()); System.out.println(buffer.getChar()); System.out.println(buffer.getShort()); } }
MappedByteBuffer
Friendly tip: the code execution is completed, and the data viewed in the idea has not changed. Go to the file management system of the computer to check. The data inside is changed.
NIO also provides MappedByteBuffer, which allows files to be modified directly in memory (memory outside the heap). NIO completes the synchronization of files without one copy. Note that here is the meaning of 5, and only 5 bytes can be modified at most
package com.atguigu.nio; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; /* explain 1. MappedByteBuffer The file can be modified directly in memory (off heap memory), and the operating system does not need to copy it once */ public class MappedByteBufferTest { public static void main(String[] args) throws Exception { RandomAccessFile randomAccessFile = new RandomAccessFile("1.txt", "rw"); //Get the corresponding channel FileChannel channel = randomAccessFile.getChannel(); /** * Parameter 1: filechannel MapMode. READ_ Read / write mode used by write * Parameter 2:0: the starting position can be modified directly * Parameter 3: 5: is the size mapped to memory (not the index position), i.e. 1 How many bytes of TXT are mapped to memory * The range that can be directly modified is 0-5. Note that 5 is not included * Actual type DirectByteBuffer */ MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 15); mappedByteBuffer.put(0, (byte) 'H'); mappedByteBuffer.put(3, (byte) '9'); mappedByteBuffer.put(14, (byte) 'Y');//IndexOutOfBoundsException randomAccessFile.close(); System.out.println("Modified successfully~~"); } }
Convert Buffer to read-only Buffer
Friendly tip: readonlybuffer Get(), if String is stored in the obtained content, it obtains ASC code. If number is stored, it obtains actual number.
package com.atguigu.nio; import java.nio.ByteBuffer; public class ReadOnlyBuffer { public static void main(String[] args) { //Create a buffer ByteBuffer buffer = ByteBuffer.allocate(64); for(int i = 0; i < 64; i++) { buffer.put((byte)i); } //read buffer.flip(); //Get a read-only Buffer ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer(); System.out.println(readOnlyBuffer.getClass()); //read while (readOnlyBuffer.hasRemaining()) { System.out.println(readOnlyBuffer.get()); } readOnlyBuffer.put((byte)100); //ReadOnlyBufferException } }
Scattering and Gathering
NIO also supports reading and writing through multiple buffers (i.e. Buffer arrays), mainly because the internal parameters of the corresponding CHANLE read and write methods can make the Buffer array object
Code for Gathering
public class TestGatheringWrites { public static void main(String[] args) { ByteBuffer b1 = StandardCharsets.UTF_8.encode("hello"); ByteBuffer b2 = StandardCharsets.UTF_8.encode("world"); ByteBuffer b3 = StandardCharsets.UTF_8.encode("Hello"); try (FileChannel channel = new RandomAccessFile("words2.txt", "rw").getChannel()) { channel.write(new ByteBuffer[]{b1, b2, b3}); } catch (IOException e) { } } }
Code for Scattering
public class TestScatteringReads { public static void main(String[] args) { try (FileChannel channel = new RandomAccessFile("words.txt", "r").getChannel()) { ByteBuffer b1 = ByteBuffer.allocate(3); ByteBuffer b2 = ByteBuffer.allocate(3); ByteBuffer b3 = ByteBuffer.allocate(5); channel.read(new ByteBuffer[]{b1, b2, b3}); b1.flip(); b2.flip(); b3.flip(); debugAll(b1); debugAll(b2); debugAll(b3); } catch (IOException e) { } } }
Network programming code:
Friendly tips:
Scattering: when writing data to the buffer, you can use the buffer array to write [scattered] successively
Gathering: when reading data from the buffer, you can use the buffer array to read data in sequence
package com.atguigu.nio; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Arrays; /** * Scattering: When writing data to the buffer, you can use the buffer array to write [scattered] successively * Gathering: When reading data from the buffer, you can use the buffer array to read data in sequence */ public class ScatteringAndGatheringTest { public static void main(String[] args) throws Exception { //Using ServerSocketChannel and SocketChannel networks ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); InetSocketAddress inetSocketAddress = new InetSocketAddress(7000); //Bind the port to the socket and start serverSocketChannel.socket().bind(inetSocketAddress); //Create buffer array ByteBuffer[] byteBuffers = new ByteBuffer[2]; byteBuffers[0] = ByteBuffer.allocate(5); byteBuffers[1] = ByteBuffer.allocate(3); //Wait for client connection (telnet) SocketChannel socketChannel = serverSocketChannel.accept(); int messageLength = 8; //Assume 8 bytes are received from the client //Cyclic reading while (true) { int byteRead = 0; while (byteRead < messageLength ) { // Read data from client long l = socketChannel.read(byteBuffers); byteRead += l; //Cumulative bytes read System.out.println("byteRead=" + byteRead); //Use stream printing to see the position and limit of the current buffer Arrays.asList(byteBuffers).stream().map(buffer -> "postion=" + buffer.position() + ", limit=" + buffer.limit()).forEach(System.out::println); } //flip all buffer s Arrays.asList(byteBuffers).forEach(buffer -> buffer.flip()); //Read and display data to the client long byteWirte = 0; while (byteWirte < messageLength) { long l = socketChannel.write(byteBuffers); // byteWirte += l; } //clear all buffer s Arrays.asList(byteBuffers).forEach(buffer-> { buffer.clear(); }); System.out.println("byteRead:=" + byteRead + " byteWrite=" + byteWirte + ", messagelength" + messageLength); } } }
Selectors are also called multiplexers
Tips: one Selector corresponds to multiple channels A thread corresponds to a Selector. The Selector is used to listen to Channel events. Only when an event occurs will it be processed. There is no or no blocking. What should this thread do. The high efficiency of redis single thread is also the problem of IO multiplexing.
Schematic diagram:
socket: indicates the client. No Channel is written in the middle (corresponding to read-write). Multiple channels correspond to a Selector. Similarly, it corresponds to a thread.
characteristic:
1. Netty's NioEventLoop aggregates Selector selectors.
2. If the current channel is idle, the thread will switch to other channels to perform tasks.
Selector class related methods
The difference between SelectedKeys() and Select(): SelectedKeys() is an acquisition, which will be returned as long as it is registered on the Selector, and Select() returns the Selectionkey with an event
Supplement:
When the Selector's select is listening, if no event occurs, it is in the blocking state. If an event occurs, it is not in the blocking state.
The select() method of the Selector will listen to the channel where the event occurs. Once an event occurs, add its corresponding selectionkey to the internal set and return it. The internal set corresponds to the following figure. HashSet<SelectionKey>
Another Channel is associated with the SelectionKey
selector.select() / / blocking is also a blocking method
selector.select(1000);// Block for 1000 ms and return after 1000 ms
selector.wakeup();// Wake up selector
selector.selectNow();// No blocking, return immediately
When does select not block
The following are all cases where the event does not block
- When the event occurs
- When a client initiates a connection request, an accept event will be triggered
- When the client sends data, the read event will be triggered when the client closes normally or abnormally. In addition, if the sent data is larger than the buffer, multiple read events will be triggered
- If the channel is writable, the write event will be triggered
- When nio bug occurs under linux
- Call selector wakeup()
- Call selector close()
- interrupt of the thread where the selector is located
Can the incident not be handled after it occurs
After an event occurs, you can either process it or cancel it. You can't do nothing. Otherwise, the event will still be triggered next time. This is because the nio bottom layer uses horizontal triggering
Schematic diagram of NIO non blocking network programming
1. When the client connects, the SocketChannel will be obtained through the ServerSocketChannel of the server, and the SocetChannel of the client can be obtained through the accept() method of ServerSocketChannel
Tip: before registering the client with the Selector, first register the ServsocketChannel of the server with the Selector, which is also the register () method called.
2. The Selector listens for events by calling the select() method of the Selector. The method returns the number of channels with events.
3. Register socketchannels on the selector, register(Selector sel, int ops). Multiple socketchannels can be registered on a selector
4. After registration, a SelectionKey will be returned, and its SelectionKey will also be put into the HashMap collection and associated with the Selector (Collection)
5. Then, the selectionkey of each client is obtained (the channel with events)
6. When the socketchannel is inversely obtained through the SelectionKey, the SelectionKey calls the method channel() to obtain the corresponding Channel Of course, you can also get the Selector through the SelectionKey, just call Selector()
7. Business processing can be completed through the obtained Channel
SelectionKey
There are four kinds of registration relationships between Selector and network channel:
SelectionKey related methods
ServerSocketChannel (mainly for connection)
ServerSocketChannel mainly listens to new client Socket connections on the server side
SocketChannel (mainly for data reading and writing)
SocketChannel, the network IO channel, is specifically responsible for reading and writing operations. NIO writes the data in the buffer to the channel, or reads the data in the channel to the buffer.
Case 1
Why does the following code use keyiterator remove()
Friendly tip: frankly, he won't take the initiative to help us delete it. If he doesn't delete it, he will always be put in the collection. If there is no deletion, we can still get the null pointer exception, because the SelectionKey has been used by us before.
After the select event occurs, the relevant keys will be put into the selectedKeys collection, but will not be removed from the selectedKeys collection after processing. We need to code and delete them ourselves. for example
- The accept event was triggered for the first time and was not removed
- The read event is triggered the second time, but there is the last one in the selectedKeys. During processing, because no real serverSocket is connected, null pointer exception will be caused
Function of cancel
cancel cancels the channel registered on the selector and deletes the key from the keys collection. The subsequent events will not be monitored
Friendly tips: the following cases are listed to distinguish between selectors Keys () and selector selectedKeys()
1,selector.keys() mainly gets all the events registered on the Selector. Including server registered
2,selector.selectedKeys() indicates the number of events that have occurred, that is, the number of events that are occurring.
Set<SelectionKey> keys=selector.keys(); Set<SelectionKey> selectionKeys = selector.selectedKeys();
Case requirements:
1) Write a NIO entry case to realize simple data communication between server and client (non blocking)
2) Objective: to understand NIO non blocking network programming mechanism
Server code:
package com.atguigu.nio; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.util.Iterator; import java.util.Set; public class NIOServer { public static void main(String[] args) throws Exception { // Create serversocketchannel - > ServerSocket ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // Get a Selecor object Selector selector = Selector.open(); // Bind a port 6666 to listen on the server side serverSocketChannel.socket().bind(new InetSocketAddress(6666)); // Set to non blocking serverSocketChannel.configureBlocking(false); // Register the serverSocketChannel with the selector, and the concerned event is OP_ACCEPT serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("Registered selectionkey quantity=" + selector.keys().size()); // 1 // Loop waiting for client connection while (true) { // Here, we wait for 1 second. If no event occurs, we return the call of select, indicating the number of events if (selector.select(1000) == 0) { // No events occurred System.out.println("The server waited for 1 second and there was no connection"); continue; } // If the returned is > 0, the relevant selectionKey collection is obtained // 1. If the returned > 0, it indicates that the event of interest has been obtained // 2. selector.selectedKeys() returns a collection of events of interest // Reverse channel acquisition through selectionKeys Set<SelectionKey> selectionKeys = selector.selectedKeys(); System.out.println("selectionKeys quantity = " + selectionKeys.size()); // Traverse set < selectionkey >, and use iterator to traverse Iterator<SelectionKey> keyIterator = selectionKeys.iterator(); while (keyIterator.hasNext()) { // Get SelectionKey SelectionKey key = keyIterator.next(); // Handle the events according to the channel corresponding to the key if (key.isAcceptable()) { // If OP_ACCEPT, there is a new client connection // The client generates a SocketChannel SocketChannel socketChannel = serverSocketChannel.accept(); System.out.println("Client connection successfully generated a socketChannel " + socketChannel.hashCode()); // Set SocketChannel to non blocking socketChannel.configureBlocking(false); // Register the socketChannel with the selector, and the attention event is OP_READ, and give to the socketChannel at the same time // Associate a buf fer, which is on the server side socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024)); // selector.keys() in keys() represents all // selector.selectedKeys() in selectedKeys() listens for the number of events in the registered channel System.out.println("After the client connects, the registered selectionkey quantity=" + selector.keys().size()); // 2,3,4.. } if (key.isReadable()) { // Occurrence of OP_READ // Reverse obtain the corresponding channel through the key SocketChannel channel = (SocketChannel) key.channel(); // Get the buffer associated with the channel // attachment gets the data associated with it ByteBuffer buffer = (ByteBuffer) key.attachment(); channel.read(buffer); System.out.println("form client " + new String(buffer.array())); } // Manually move the current selectionKey from the collection to prevent repeated operations keyIterator.remove(); } } } }
Client code:
package com.atguigu.nio; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; public class NIOClient { public static void main(String[] args) throws Exception{ //Get a network channel SocketChannel socketChannel = SocketChannel.open(); //Set non blocking socketChannel.configureBlocking(false); //Provide server-side ip and port InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666); //Connect to the server. If the connection is not successful if (!socketChannel.connect(inetSocketAddress)) { while (!socketChannel.finishConnect()) { System.out.println("Because the connection takes time, the client will not block and can do other work.."); } } //... If the connection is successful, send the data String str = "hello, Shang Silicon Valley~"; //Wraps a byte array into a buffer // This is the buffer of the client. Wrap stores the data in the cache intelligently. It was set manually before. In the past, you need to specify the size of the buffer yourself. With it, you can be more intelligent ByteBuffer buffer = ByteBuffer.wrap(str.getBytes()); //Send data and write buffer data to channel socketChannel.write(buffer); System.in.read(); } }
Case 2 group chat
1) Write a NIO group chat system to realize simple data communication (non blocking) between server and client
2) Realize multi crowd chat
3) Server side: it can monitor users online and offline, and realize message forwarding function
4) Client: it can send messages to all other users through the channel without blocking, and can accept messages sent by other users (forwarded by the server)
Server code
package com.atguigu.nio.groupchat; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.util.Iterator; public class GroupChatServer { //Define properties private Selector selector; private ServerSocketChannel listenChannel; private static final int PORT = 6667; //constructor //Initialization work public GroupChatServer() { try { //Get selector selector = Selector.open(); //ServerSocketChannel listenChannel = ServerSocketChannel.open(); //Binding port listenChannel.socket().bind(new InetSocketAddress(PORT)); //Set non blocking mode listenChannel.configureBlocking(false); //Register the listenChannel with the selector listenChannel.register(selector, SelectionKey.OP_ACCEPT); }catch (IOException e) { e.printStackTrace(); } } //monitor public void listen() { System.out.println("Listen Thread : " + Thread.currentThread().getName()); try { //Cyclic processing while (true) { int count = selector.select(); if(count > 0) {//Event handling //Traversal to get selectionKey collection Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { //Remove the selectionkey SelectionKey key = iterator.next(); //Listen to accept if(key.isAcceptable()) { SocketChannel sc = listenChannel.accept(); sc.configureBlocking(false); //Register the sc with seletor sc.register(selector, SelectionKey.OP_READ); //Tips System.out.println(sc.getRemoteAddress() + " go online "); } if(key.isReadable()) { //The channel sends a read event, that is, the channel is in a readable state //Processing read (special write method..) readData(key); } //Delete the current key to prevent repeated processing iterator.remove(); } } else { System.out.println("wait for...."); } } }catch (Exception e) { e.printStackTrace(); }finally { //Exception handling occurred } } //Read client messages private void readData(SelectionKey key) { //Get the associated chanle SocketChannel channel = null; try { //Get channel channel = (SocketChannel) key.channel(); //Create buffer ByteBuffer buffer = ByteBuffer.allocate(1024); int count = channel.read(buffer); //Process according to the value of count if(count > 0) { //Convert the data in the cache into a string String msg = new String(buffer.array()); //Output the message from the console System.out.println("form client: " + msg); //Forward messages to other clients (get rid of yourself) and write a method to deal with it sendInfoToOtherClients(msg, channel); } }catch (IOException e) { try { System.out.println(channel.getRemoteAddress() + " Offline.."); //Unregister key.cancel(); //Close channel channel.close(); }catch (IOException e2) { e2.printStackTrace();; } } } //Forward messages to other customers (channels) private void sendInfoToOtherClients(String msg, SocketChannel self ) throws IOException{ System.out.println("Server forwarding message..."); System.out.println("The server forwards data to the client thread: " + Thread.currentThread().getName()); //Traverse all socketchannels registered on the selector and exclude self for(SelectionKey key: selector.keys()) { //Retrieve the corresponding SocketChannel through the key Channel targetChannel = key.channel(); //Exclude yourself if(targetChannel instanceof SocketChannel && targetChannel != self) { //transformation SocketChannel dest = (SocketChannel)targetChannel; //Store msg in buffer ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes()); //Write buffer data to channel dest.write(buffer); } } } public static void main(String[] args) { //Create server object GroupChatServer groupChatServer = new GroupChatServer(); groupChatServer.listen(); } } //You can write a Handler class MyHandler { public void readData() { } public void sendInfoToOtherClients(){ } }
Client code
package com.atguigu.nio.groupchat; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Scanner; import java.util.Set; public class GroupChatClient { //Define related properties private final String HOST = "127.0.0.1"; // Server ip private final int PORT = 6667; //Server port private Selector selector; private SocketChannel socketChannel; private String username; //Constructor to complete initialization public GroupChatClient() throws IOException { selector = Selector.open(); //Connect server socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1", PORT)); //Set non blocking socketChannel.configureBlocking(false); //Register the channel with the selector socketChannel.register(selector, SelectionKey.OP_READ); //Get username username = socketChannel.getLocalAddress().toString().substring(1); System.out.println(username + " is ok..."); } //Send message to server public void sendInfo(String info) { info = username + " Say:" + info; try { socketChannel.write(ByteBuffer.wrap(info.getBytes())); }catch (IOException e) { e.printStackTrace(); } } //Read the message replied from the server public void readInfo() { try { int readChannels = selector.select(); if(readChannels > 0) {//There are channels available Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); if(key.isReadable()) { //Get the relevant channel SocketChannel sc = (SocketChannel) key.channel(); //Get a Buffer ByteBuffer buffer = ByteBuffer.allocate(1024); //read sc.read(buffer); //Convert the read buffer data into a string String msg = new String(buffer.array()); System.out.println(msg.trim()); } } iterator.remove(); //Delete the current selectionKey to prevent repeated operations } else { //System.out.println("no channel available...); } }catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception { //Start our client GroupChatClient chatClient = new GroupChatClient(); //Start a thread every 3 seconds to read the data sent from the server new Thread() { public void run() { while (true) { chatClient.readInfo(); try { Thread.currentThread().sleep(3000); }catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); //Send data to the server Scanner scanner = new Scanner(System.in); while (scanner.hasNextLine()) { String s = scanner.nextLine(); chatClient.sendInfo(s); } } }
Boundary of processing message (brief introduction)
That is, the problem of sticking package, which will be discussed in detail later. Just a brief introduction
Due to the uncertainty of the network, a message is split into incomplete, or a message is complete but has more messages that do not belong to itself at the end.
Solution:
- Idea 1: fixed message length, the same packet size, and the server reads according to the predetermined length. The disadvantage is a waste of bandwidth
- Train of thought 2: split by separator. The disadvantage is low efficiency
- Idea 3: TLV format, i.e. Type, Length and Value data. When the Type and Length are known, it is convenient to obtain the message size and allocate the appropriate buffer. The disadvantage is that the buffer needs to be allocated in advance. If the content is too large, it will affect the server throughput
- Http 1.1 is a TLV format
- Http 2.0 is an LTV format
This idea is a little like the internal structure of Java class file