Netty study notes
1. Introduction and application scenario of netty
1.1 INTRODUCTION
- Netty is an open source framework for jboss
- Netty is an asynchronous, event driven network application framework
- Based on nio
1.2 application scenarios
- Rpc e.g. dubbo
- game
- big data
Applications involving network communication can use netty
2. i/o model
2.1 introduction
- bio synchronizes and blocks a connection, corresponding to a server thread. It is applicable to the architecture with less connections jdk1 four
- nio synchronous non blocking server processes multiple connections in one thread, which is suitable for a large number of connections and a short connection time jdk1 four
- aio(nio.2) asynchronous non blocking is applicable to jdk1 seven
2.2 bio
blocking i/o
2.2.1 simple demo
Develop a server and create a thread pool. When the client sends a request, the server will create a thread for processing. When there are multiple client requests, multiple threads will be created for processing
Here, the demo client is simulated with telnet
public static void handler(Socket socket){ try(InputStream in = socket.getInputStream();){ System.out.println("Thread information: id "+Thread.currentThread().getId()+" name " + Thread.currentThread().getName()); byte[] bytes = new byte[1024]; while (true){ int read = in.read(bytes); if(read!=-1){ System.out.println("Output information: "+new String(bytes,"UTF-8")); }else { break; } } }catch (IOException e){ e.printStackTrace(); } } public static void main(String[] args) { try(ServerSocket serverSocket = new ServerSocket(6666);) { ExecutorService executorService = Executors.newCachedThreadPool(); System.out.println("Thread information: id "+Thread.currentThread().getId()+" name " + Thread.currentThread().getName()); while (true){ System.out.println("Wait for link"); final Socket socket = serverSocket.accept(); System.out.println("Link to a client"); executorService.execute(new Runnable() { @Override public void run() { System.out.println("Thread information: id "+Thread.currentThread().getId()+" name " + Thread.currentThread().getName()); handler(socket); } }); } } catch (IOException e) { e.printStackTrace(); } }
2.3 nio
Non blocking I / O non blocking
2.3.1 introduction
Three cores
- channel
- Buffer buffer
- Selector selector
Briefly describe the operating principle: the selector selects the available channels. Channels and buffers can read and write to each other. The application does not directly operate the channel, but indirectly operates the channel by operating the buffer
There will be multiple selectors in a thread. Multiple channel s can be registered in a selector. If there is no data transmission, the thread can do other things and will not wait all the time
2.4 difference between NiO and bio
- nio non blocking bio blocking
- nio processes io in block mode, and bio processes io in stream mode, which is faster than stream mode
- bio is based on byte stream / character stream, nio is based on buffer and channel selector to listen for events of multiple channels, so one thread can process data of multiple channels
Illustration: nio
bio
3. nio detailed explanation
3.1 relationship among three components of NiO model
- A thread corresponds to a selector
- One selecor corresponds to multiple channel s
- A channel corresponds to a buffer
- One thread corresponds to multiple channel s
- Both channel and buffer are bidirectional, that is, they can be read or written. Use the flip() method to switch
- buffer is a memory block, which is faster to read and write memory
- The selector switches different channel s according to different events
3.2 Buffer buffer
3.2.1 introduction
It is essentially a memory block for reading and writing data, which can be understood as a container object (array) that provides methods for operating memory blocks
Some mechanisms are built in the buffer, which can detect the data change and state change of the buffer
The data read and written in the channel must pass through the Buffer
3.2.2 source code analysis
Several common operation methods
public static void main(String[] args) { //allocate specifies the length of the intbuffer IntBuffer buffer = IntBuffer.allocate(5); //capacity() get capacity //put() write for(int i = 0;i<buffer.capacity();i++){ buffer.put(i*2); } //flip() reverses from write to read buffer.flip(); //read //get() moves the index back one bit after each read for(int i = 0;i<buffer.capacity();i++){ System.out.println(buffer.get()); } }
3.2.2.1 definitions
An int array is defined in IntBuffer, which is similar to other types of buffers
public abstract class IntBuffer extends Buffer implements Comparable<IntBuffer> { // These fields are declared here rather than in Heap-X-Buffer in order to // reduce the number of virtual method invocations needed to access these // values, which is especially costly when coding small buffers. // final int[] hb; // Non-null only for heap buffers final int offset; boolean isReadOnly; // Valid only for heap buffers
Four attributes are defined in the Buffer class at the top level
public abstract class Buffer { /** * The characteristics of Spliterators that traverse and split elements * maintained in Buffers. */ static final int SPLITERATOR_CHARACTERISTICS = Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED; // Invariants: mark <= position <= limit <= capacity private int mark = -1; //sign private int position = 0; //The position of the current index cannot exceed the limit private int limit;//Maximum length that can be read and written private int capacity;//Length of capacity allocate definition
3.2.2.2 reverse rotation
public final Buffer flip() { limit = position; position = 0; mark = -1; return this; }
You can see that after the reversal, it changes from read to write, or from write to read
Set the index to 0. The maximum read-write length cannot exceed the index of the last operation
3.2 channel
3.2.1 introduction
- Channels are similar to streams / connections, but streams can only be written or read, and channels can be read and written
- Channel asynchronous read / write data
- The channel can read and write data to the cache
3.2.2 hierarchical relationship
When a client sends a request, the server will create a ServerSocketChannel (implementation class: ServerSocketChannelImpl), and then the ServerSocketChannel will create a SocketChannel (implementation class: SocketChannelImpl is the channel that really reads and writes data). This SocketChannel corresponds to the client request
3.2.3 case analysis
3.2.3.1 filechanle output file stream
import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; /** * @author: zhangyao * @create:2020-08-25 14:50 **/ public class FileChannelTest { public static void main(String[] args) { FileOutputStream fileOutputStream = null; try { //File output stream fileOutputStream = new FileOutputStream("D:\\file01.txt"); //The file output stream is wrapped as FileChannel, where FileChannel implements FileChannelImpl by default FileChannel fileChannel = fileOutputStream.getChannel(); //Create corresponding buffer ByteBuffer byteBuffer = ByteBuffer.allocate(1024); //Data write buffer byteBuffer.put("hello nio".getBytes()); //Reverse, because you need to read data from the buffer and write it to the Channel byteBuffer.flip(); //Write Channel from buffer fileChannel.write(byteBuffer); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { //Close file stream if(fileOutputStream!=null){ try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
The overall process is to write the data into the buffer, write the data into the Channel in the read buffer, and output the data from the file output stream
The illustration is as follows
3.2.3.2 filechannel input file stream
public static void main(String[] args) { FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream("D:\\file01.txt"); //Get Channel FileChannel channel = fileInputStream.getChannel(); //Create byteBuffer ByteBuffer byteBuffer = ByteBuffer.allocate(1024); //Read data from channel and write to buffer channel.read(byteBuffer); //In the next step, you need to read the data output from the buffer byteBuffer.flip(); //output byte[] array = byteBuffer.array(); System.out.println(new String(array)); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
Contrary to the above example, the data is read from the file, written to the buffer buffer through the channel, and output
Illustration
3.2.3.3 FileChannel copy files
In fact, it is the combination of the above two examples to copy the data in one file to another
public static void main(String[] args) { FileInputStream fileInputStream = null; FileOutputStream fileOutputStream = null; try { fileInputStream = new FileInputStream("D:\\file01.txt"); fileOutputStream = new FileOutputStream("D:\\file02.txt"); FileChannel channel = fileInputStream.getChannel(); FileChannel channel1 = fileOutputStream.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); while (true){ //Reset byteBuffer byteBuffer.clear(); int read = channel.read(byteBuffer); if(read==-1){ break; } byteBuffer.flip(); channel1.write(byteBuffer); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { fileInputStream.close(); fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
ByteBuffer is used here Clear() method
Because the ByteBuffer buffer has a length, when the read file exceeds the length of the buffer, if the buffer is not emptied, the next read will start from the last read position, resulting in an endless loop
3.2.3.4 TransferFrom of filechannel copied files
public static void main(String[] args) { FileInputStream fileInputStream = null; FileOutputStream fileOutputStream = null; try { fileInputStream = new FileInputStream("D:\\file01.txt"); fileOutputStream = new FileOutputStream("D:\\file02.txt"); FileChannel channel = fileInputStream.getChannel(); FileChannel channel1 = fileOutputStream.getChannel(); //Copy from channel channel to channel1 channel channel1.transferFrom(channel, 0, channel.size()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { fileInputStream.close(); fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
3.2.4 Buffer dispersion and aggregation
The above examples use a single buffer to read and write data. If the data is too large, you can also use multiple buffers (buffer arrays) to read and write data, that is, use space for time
3.3 Selector
3.3.1 basic introduction
One selector manages multiple channel channels and processes io in an asynchronous manner
Only when reading and writing really happen, data will be processed, which reduces the pressure of threads and eliminates the need to maintain a thread for each request
It avoids the overhead caused by context switching between multiple threads
3.3.2 api of selector
Selector:
- select() blocking
- select(Long timeout) has a timeout
- selectNow() non blocking
- wakeup() wakes up the selector immediately
3.3.2 selecor workflow
In fact, it is the working principle of selector selectionkey serversocketchannel sockcetchannel
-
When the client is linked, get the SocketChannel through ServerSockertChannel and register it with the Selector
- Registration source code
public abstract SelectionKey register(Selector sel, int ops) throws ClosedChannelException;
This is the method that SocketChannel registers on the Selector. The first parameter is the Selector object to be registered, and the second parameter is the event driven type
public abstract class SelectionKey { public static final int OP_READ = 1; public static final int OP_WRITE = 4; public static final int OP_CONNECT = 8; public static final int OP_ACCEPT = 16; private volatile Object attachment = null;
-
After registration, a selectionKey will be returned, which will be associated with SocketChannel
-
The Selector listens to the Channel through the select method. If an event occurs, it returns the corresponding selectionKey collection
-
Source code
public int select(long var1) throws IOException { if (var1 < 0L) { throw new IllegalArgumentException("Negative timeout"); } else { return this.lockAndDoSelect(var1 == 0L ? -1L : var1); } } public int select() throws IOException { return this.select(0L); } public int selectNow() throws IOException { return this.lockAndDoSelect(0L); } private int lockAndDoSelect(long var1) throws IOException { synchronized(this) { if (!this.isOpen()) { throw new ClosedSelectorException(); } else { int var10000; synchronized(this.publicKeys) { synchronized(this.publicSelectedKeys) { var10000 = this.doSelect(var1); } } return var10000; } } }
-
-
The Channel can be obtained inversely through the obtained selectionKey
-
Source code
public abstract SelectableChannel channel();
-
-
Finally, the service is processed through the channel
3.3.3 cases
Service side idea:
- Create serverSocketChannel binding port 6666 and register this channel with the Selector. The registered event is OP_ACCEPT
- Loop listening to determine whether an event occurs in the channel. If an event occurs, judge different event types for different links and read / write operations
Client ideas
- Create a SocketChannel. After connecting to the server, send messages and keep the link closed
3.3.3.1 server side
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.util.Iterator; import java.util.Set; /** * @author: zhangyao * @create:2020-08-26 16:55 **/ public class ServerChannel { public static void main(String[] args) { try { //Generate a ServerScoketChannel ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //Set to non blocking serverSocketChannel.configureBlocking(false); //serverSocket listens on port 6666 serverSocketChannel.socket().bind(new InetSocketAddress(6666)); //Create Selector Selector selector = Selector.open(); //serverSocketChannel registers with the Selector SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); //Loop wait link while (true){ //If no event occurs, the loop continues if(selector.select(1000) == 0){ System.out.println("Wait 1 s,No connection"); continue; } //If you have event driven, you need to traverse events Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()){ SelectionKey key = iterator.next(); //If the event is a connection if(key.isAcceptable()){ try { SocketChannel channel = serverSocketChannel.accept(); channel.configureBlocking(false); SelectionKey register = channel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024)); System.out.println("Link successful"); } catch (IOException e) { e.printStackTrace(); } } //If reading data if(key.isReadable()){ SocketChannel channel = (SocketChannel) key.channel(); ByteBuffer byteBuffer = (ByteBuffer) key.attachment(); try { int read = channel.read(byteBuffer); byte[] array = byteBuffer.array(); System.out.println("Read data:"+ new String(byteBuffer.array())); } catch (IOException e) { e.printStackTrace(); } } iterator.remove(); }; } } catch (IOException e) { e.printStackTrace(); } } }
3.3.3.2 client
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; /** * @author: zhangyao * @create:2020-08-26 17:24 **/ public class ClientChannel { public static void main(String[] args) { //Create a SocketChannel try { SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666); if(!socketChannel.connect(inetSocketAddress)){ while (!socketChannel.finishConnect()){ System.out.println("Server connection in progress,Threads are not blocked,Other operations can be performed"); } } //Connection succeeded socketChannel.write(ByteBuffer.wrap("hello ,server".getBytes())); System.in.read(); } catch (IOException e) { e.printStackTrace(); } } }
4.Netty
4.1 introduction
https://netty.io/ Official website
netty is the encapsulation of java nio api, which simplifies the development of nio program. The minimum requirement of jdk is 1.6
The popular network programming communication framework, Dubbo Elasticsearch and other frameworks, and the underlying network communication framework are Netty
Architecture model
edition
netty has 3 x 4. x 5. X three major versions
3.x older, 5 X has a major bug and was abandoned by the official website. Now it mainly uses 4 x
4.2 thread model
There are several Threading Models
4.2.1 traditional I/O blocking model
Each link needs a corresponding thread to process, and after the link is established, if the current link has no data transmission, this thread will be blocked in the read() method
4.2.2 Reactor mode
The principle diagram is shown above
This paper mainly improves the problem that a connection will block a thread in the traditional I/O model. When the connection is established, it will be processed by calling the thread in the thread pool through ServiceHandler. In this way, only one ServiceHandler thread will be blocked to achieve the purpose of multiplexing
There are three ways to implement Reactor mode
4.2.2.1 single Reactor single thread
Use one thread to multiplex all operations, including read-write connections
redis uses this model, single thread
4.2.2.2 single Reactor multithreading
Compared with single Reactor and single thread, the main thread does not conduct business processing. When a request comes, the specific business processing is handed over to the thread in the thread pool for processing. After the thread processing is completed, it is returned to the Client through the handler
4.2.2.3 master slave Reactor multithreading
Compared with single Reacotr, the master-slave Reactor divides the Reactor into main Reactor and SubReactor
MainReactor is responsible for distribution and connection
SubReactor is responsible for reading and writing
One MainReactor can correspond to multiple subreactors
4.2.3 Netty model
Briefly describe Netty model
-
role
- Bossgroup the type of bossgroup is NioEventLoopGroup, which contains many nioeventloops
- NioEventLoop nio event loop. Each NioEventLoop has a Selctor and a task queue
- The type of WorkerGroup is NioEventLoopGroup, which is similar to BossGroup, but with different functions. BossGroup is only responsible for establishing a connection with the client. WorkerGroup needs to read and write and handle business
- The PipeLine encapsulates the Channel. The specific business processing is to process the Channel through the PipeLine
-
Specific process
- When the client sends a request, it first enters the BossGroup. NioEventLoop polls the request for events. If it is a connection event, it will be processed
- The processing steps are divided into three steps
- polling
- Registration here refers to registering the generated SocketChannel with a Selector in a NioEventLoop in the workerGroup
- Execute task list
- When the requested event is read-write, the worker group performs specific business processing on the request
- The processing steps are similar to those of BossGroup
- When the client sends a request, it first enters the BossGroup. NioEventLoop polls the request for events. If it is a connection event, it will be processed
-
summary
It can be seen that Netty's model is similar to the master-slave Reactor model. A master Reactor is responsible for connecting events and a slave Reactor is responsible for reading and writing events
4.2.4 case demo
4.2.4.1 server
4.2.4.1.1 NettyServer
package netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; /** * @author: zhangyao * @create:2020-09-03 08:55 **/ public class NettyServer { public static void main(String[] args) { //Create BossGroup and WorkerGroup EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap serverBootstrap = new ServerBootstrap(); ChannelFuture channelFuture = null; try { serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) { socketChannel.pipeline().addLast(new NettyServerHandler()); } }); System.out.println("Server ready....."); //Binding port channelFuture = serverBootstrap.bind(6668).sync(); }catch (Exception e){ e.printStackTrace(); }finally { try { channelFuture.channel().closeFuture().sync(); bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
4.2.4.1.2 NettyServerHandler
package netty; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.CharsetUtil; /** * @author: zhangyao * @create:2020-09-03 09:12 **/ public class NettyServerHandler extends ChannelInboundHandlerAdapter { //Read data @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; System.out.println("Client send message:"+ buf.toString(CharsetUtil.UTF_8)); System.out.println("Client address:"+ ctx.channel().remoteAddress()); } //Data read complete @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(Unpooled.copiedBuffer("hello client",CharsetUtil.UTF_8)); } //Handle exception close ctx @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
4.2.4.2 client
4.2.4.2.1 NettyClient
package netty; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; /** * @author: zhangyao * @create:2020-09-03 09:52 **/ public class NettyClient { public static void main(String[] args) { EventLoopGroup executors = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); try { bootstrap.group(executors) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new NettyClientHandler()); } }); System.out.println("Client ready........"); ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync(); //Close channel channelFuture.channel().closeFuture().sync(); }catch (Exception e){ e.printStackTrace(); }finally { try { executors.shutdownGracefully().sync(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
4.2.4.2.2 NettyClientHandler
package netty; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.CharsetUtil; /** * @author: zhangyao * @create:2020-09-03 10:00 **/ public class NettyClientHandler extends ChannelInboundHandlerAdapter { //Triggered when ready @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("ctx: "+ctx); ctx.writeAndFlush(Unpooled.copiedBuffer("hello,Server", CharsetUtil.UTF_8)); } //Read information //The information returned by the server is read here @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf byteBuf = (ByteBuf) msg; System.out.println("The server sends a message: "+ byteBuf.toString(CharsetUtil.UTF_8)); System.out.println("Server address: "+ ctx.channel().remoteAddress()); } //exception handling @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
A simple TCP service communication, the client sends messages, the server receives messages and returns messages to the client
4.2.4.3 case demo source code analysis
4.2.4.3.1 NioEventGroup
public NioEventLoopGroup() { this(0); } public NioEventLoopGroup(int nThreads) { this(nThreads, (Executor)null); }
You can see that if you use the parameterless NioEventGroup, the default is 0. You can also specify the number of threads
Find it layer by layer:
private static final int DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); //NettyRuntime.availableProcessors() gets the number of cores (logical processors) of the current computer protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) { super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args); }
Finally, find the method of NioEventGroup parent class
If the number of threads of NioEventGroup is specified and is not 0, the specified number of threads will be used
Otherwise, use the number of cores * 2 of the current computer as the number of threads
debug to see the results
The computer has 12 cores and 24 threads by default
Specify a thread
There is only one thread
4.2.5 asynchronous model
ChannelFuture in the above case Demo
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();
The asynchronous model Future is relative to synchronization
Asynchrony means that when an asynchronous call is issued, it will not get the result immediately, but notify the caller of the call result through callback and status
The connect and bind() sync methods in Netty return an asynchronous result, and then get the result through listening
That is, the future listener mechanism
When the Future object is just created, it is in an incomplete state. You can view the operation execution status through the returned ChannelFuture, or register a listening function to execute the completed operation
Is isSucess() successful
Is isDone() complete
isCancelable() cancel
cause() failure reason
addListener add listener
//Binding port channelFuture = serverBootstrap.bind(6668).sync(); channelFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { if(channelFuture.isSuccess()){ System.out.println("Listening port succeeded"); }else { System.out.println("Listening port failed"); } } });
4.2.6 Netty Http service
Make a simple demo. The browser (client) accesses the 7001 port of the server and returns a string information
Browser access is an http request, and the server also needs a corresponding httpResponse
4.2.6.1 server
NettyHttpServer startup class
package netty.http; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; /** * @author: zhangyao * @create:2020-09-04 11:16 **/ public class NettyHttpServer { public static void main(String[] args) throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new NettyHttpInitialize()); ChannelFuture channelFuture = serverBootstrap.bind(7001).sync(); channelFuture.channel().closeFuture().sync(); bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }
NettyHttpInitialize processor class
Encapsulate the previous ChannelInitialize(SocketChannel)
package netty.http; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpServerCodec; /** * @author: zhangyao * @create:2020-09-04 11:21 **/ public class NettyHttpInitialize extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel sc) throws Exception { //Get pipe ChannelPipeline pipeline = sc.pipeline(); //The processor added to the pipeline is mainly used to process Http requests and parse request headers pipeline.addLast("myDecoder",new HttpServerCodec()); //Add processor pipeline.addLast("myServerHandler",new NettyServerHandler()); } }
Specific processing of NettyServerHandler (return http response)
package netty.http; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.*; import io.netty.util.CharsetUtil; import org.springframework.http.HttpStatus; /** * @author: zhangyao * @create:2020-09-04 14:01 **/ public class NettyServerHandler extends SimpleChannelInboundHandler<HttpObject> { //read message @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject) throws Exception { System.out.println(httpObject); System.out.println("Client address+"+ channelHandlerContext.channel().remoteAddress()); ByteBuf byteBuf = Unpooled.copiedBuffer("hello , im Server", CharsetUtil.UTF_8); //Return client information FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.OK,byteBuf); fullHttpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8"); fullHttpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH,byteBuf.readableBytes()); channelHandlerContext.writeAndFlush(fullHttpResponse); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
Record of problems:
-
The first time you bind port 6668, the browser access fails. Just change to 7001
Reason: Google browser has disabled 6665-6669 and some other insecure ports
-
The first request after access is abnormal, and the data can be returned normally
Cause: exception handling method is not overridden in NettyServerHandler
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); }
Add this part, and the error information is obvious
4.2.6.2 filtering of HTTP services
You can filter some requests that you don't want to process. In fact, you can judge the uri of the request during the processing of http requests
Add the following code to the NettyServerHandler class above to intercept / favicon ICO request
HttpRequest re = (HttpRequest) httpObject; String uri = re.uri(); if(uri.equals("/favicon.ico")){ System.out.println("I don't want to deal with it,return"); return; }
4.3 Netty API sorting
Based on the above demo s, this paper systematically combs the classes and methods commonly used by Netty
4.3.1 Bootstrap
-
ServerBootstrap server boot class
BootStrap client boot class
- . group() set NioEventLoopGroup for BootStrap, and multiple can be set
- . channel() sets the channel class used by the service
- . option() set channel parameters
- . handler() sets the BossGroup
- . childrenHandler() sets the workerGroup
- . bind() bind a port number to the server and listen on the port
- The connect() client is used to connect to the server
4.3.2 Future
io operations in Netty are asynchronous, that is, they cannot return results immediately, but notify the caller when they are completed
-
Future
-
ChannelFutrue
method
- channel() returns the channel for which the IO operation is currently in progress
- sync() turns to synchronization and waits for the asynchronous operation to complete
4.3.3 Channel
Different protocols and different blocking types have corresponding channels
- NioSocketChannel asynchronous tcp protocol Socket connection
- NioServerSocketChannel asynchronous tcp protocol server connection
- NioDatagramChannel asynchronous udp connection
- NioSctpChannel asynchronous sctp client connection
- NioSctpServerChannel asynchronous sctp server connection
4.3.4 Selector
Netty implements multiplexing based on Nio Selector object. One selector manages multiple channel s
4.3.5 ChannelHandler
It is mainly used for data processing. There are many encapsulated methods, which can inherit their subclasses when used
Implementation class
There are many subclasses, including several commonly used ones
channelHandler
- ChannelInboundHandler
- ChannelOutboundHandler
- Adapter
- channelInboundHandlerAdapter
- channelOutboundHandlerAdapter
4.3.6 pipeline
The structure diagram is as above
A channelpipeline can be created in the channel, and a two-way linked list composed of ChannelHandlerContext is maintained in the channelpipeline
Each ChannelHandlerContext corresponds to a Channelhandler
Common methods:
addFirst(); Add a Handler to the first position in the linked list
addLast(); Add to the last position in the linked list
4.3.7 channelHandlerContext
Each channelhandlerContext contains a channelhandler (business processing)
The corresponding channel and pipeline information can also be obtained in the channelHandlerContext
channelHandlerContext.channel();
channelHandlerCOntext.pipeline();
4.3.8 EventLoopGroup
netty generally provides two eventloopgroups, bosseventloopgroup and workerEventLoopGroup
EventLoopGroup can specify how many cores to use
4.3.9 Unplooed
A utility class provided by Netty to manipulate buffer data
Common methods:
copiedBuffer(); The Bytebuf object provided by Netty is returned
4.3.10 ByteBuf
Netty's data container (buffer)
You can read / write directly. There is no need to flip() between reads and writes because ByteBuf internally maintains two indexes readindex and writeindex
common method
getByte()
readByte()
writeByte()
capacity()
4.4 Netty heartbeat detection mechanism
When the client has no read / write operation for a long time, the server needs to detect whether the client is still connected, that is, heartbeat detection
Netty provides the processing class IdleStateHandler for heartbeat detection
Sample code
package netty.hearbeat; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.ServerSocketChannel; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.timeout.IdleStateHandler; import java.util.concurrent.TimeUnit; /** * @author: zhangyao * @create:2020-09-10 10:04 **/ public class MyServer { public static void main(String[] args) throws Exception{ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup,workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler()) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new IdleStateHandler(3,5,7, TimeUnit.SECONDS)); pipeline.addLast("inleHandler",new MyHearBeatHandler()); } }); ChannelFuture channelFuture = serverBootstrap.bind(8090).sync(); channelFuture.channel().closeFuture().sync(); bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }
package netty.hearbeat; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandler; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; /** * @author: zhangyao * @create:2020-09-10 16:50 **/ public class MyHearBeatHandler extends ChannelInboundHandlerAdapter { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } @Override public void userEventTriggered(ChannelHandlerContext channelHandlerContext, Object o) throws Exception { if(o instanceof IdleStateEvent){ IdleStateEvent event = (IdleStateEvent) o; IdleState state = event.state(); switch (state){ case READER_IDLE: System.out.println("Read idle"); break; case WRITER_IDLE: System.out.println("Write idle"); break; case ALL_IDLE: System.out.println("Read write idle"); break; } } } }
There is no difference between clients
4.5 Netty's webSocket
Writing webSocket long connection using netty
4.5.1 server
package netty.websocket; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.handler.stream.ChunkedWriteHandler; /** * @author: zhangyao * @create:2020-09-11 14:43 **/ public class MyServer { public static void main(String[] args) throws Exception{ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) { ChannelPipeline pipeline = socketChannel.pipeline(); //Add http decoder pipeline.addLast(new HttpServerCodec()); //Add block transport processor pipeline.addLast(new ChunkedWriteHandler()); //http segmented transmission, adding an aggregation processing pipeline.addLast(new HttpObjectAggregator(8192)); //Add websocket protocol processing pipeline.addLast(new WebSocketServerProtocolHandler("/hello")); //Add custom processing business processor pipeline.addLast(new MyServerHandler()); } }); ChannelFuture channelFuture = serverBootstrap.bind(7000).sync(); channelFuture.channel().closeFuture().sync(); bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }
4.5.2 customized processor on the server
package netty.websocket; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; /** * @author: zhangyao * @create:2020-09-11 14:49 **/ public class MyServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { //When the client closes the connection System.out.println("Client close connection..."+ ctx.channel().id()); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { //When the client connects to the server System.out.println("A client is connected to the server id by" + ctx.channel().id()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { //Close connection abnormally ctx.close(); } //Received message @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception { //Read the message and return the same message to the client String text = textWebSocketFrame.text(); System.out.println("The server receives the message" + text); //Return to client Channel channel = channelHandlerContext.channel(); channel.writeAndFlush(new TextWebSocketFrame(LocalDateTime.now()+" Server return message:" + text)); } }
4.5.3 page (webSocket client)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>test netty+webSocket</title> </head> <body> <form onsubmit="return false"> <textarea id="sendMessage" style="height: 300px;width: 300px" placeholder="Please enter a message to send"></textarea> <button onclick="send(document.getElementById('sendMessage').value)">send message</button> <textarea id="responseMessage" style="height: 300px;width: 300px" ></textarea> <button onclick="document.getElementById('responseMessage').value=''">Clear message</button> </form> </body> <script type="application/javascript"> var websocket; if(!window.WebSocket){ alert("Browser does not support webSocket") }else { //Open and close the webSocket websocket = new WebSocket("ws://localhost:7000/hello"); //webSocket opening event //Add a piece of data to the message return box websocket.onopen = function (ev) { document.getElementById("responseMessage").value = 'Connect to the server'; } websocket.onclose = function (ev) { document.getElementById("responseMessage").value += '\n Connection closed'; } //When the server responds to the message, trigger to echo the message returned by the server to the text box websocket.onmessage = function (ev) { document.getElementById("responseMessage").value += '\n ' ; document.getElementById("responseMessage").value += ev.data ; } } //send message function send (message) { if(!window.websocket){ alert("socket Initialization has not completed yet"); return; } if(websocket.readyState == WebSocket.OPEN){ websocket.send(message); document.getElementById('sendMessage').value='' } } </script> </html>
4.6 Netty encoding and decoding
Encoding and decoding process in network transmission
Codec codec includes encoder and decoder
netty provides some codecs for stringcodec and objectcodec, but these codecs still rely on the underlying serialization technology of java. The underlying serialization technology of java is relatively inefficient, so it is necessary to introduce new and efficient serialization technology
4.6.1 ProtoBuf
4.6.2 custom codec
package netty.inboundAndOutbound; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import netty.inboundAndOutbound.client.MyClientMessageToByteHandler; /** * @author: zhangyao * @create:2020-09-18 14:53 **/ public class MyServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); //Add stack decoder pipeline.addLast(new ByteToMessageHandler()); //Add out of stack encoder pipeline.addLast(new MyClientMessageToByteHandler()); //Add custom processor pipeline.addLast(new MyServerHandler()); } }
package netty.inboundAndOutbound; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import java.util.List; /** * @author: zhangyao * @create:2020-09-18 14:54 **/ public class ByteToMessageHandler extends ByteToMessageDecoder { //Custom implemented stack decoder @Override protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception { if(byteBuf.readableBytes()>=8){ list.add(byteBuf.readLong()); } } }
package netty.inboundAndOutbound.client; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import java.util.List; /** * @author: zhangyao * @create:2020-09-18 16:16 **/ public class MyClientByteToLong extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception { if(byteBuf.readableBytes()>=8){ list.add(byteBuf.readLong()); } } }
4.6.3 handler processing mechanism and out stack in stack
Through the above encoding and decoding, it is extended to the handler processing mechanism
Simple explanation diagram of netty out of stack and into stack
Out of stack and in stack are relative. When the client sends a message to the server, it is out of stack for the client and in stack for the server, and vice versa
4.7 Tcp unpacking
4.7.1 introduction to unpacking
When Tcp service sends a message again, if it sends multiple packets With a small amount of data and a large number of packets, Tcp will combine multiple packets into a large packet through the algorithm and send it to the receiver. The problem is that the receiver cannot recognize the complete packet, and the resulting problem is packet contamination and unpacking
As shown in the figure above, the Client sends D1 and D2 packets to the Server
Four situations may occur when reading from the Server side
1. The data packets D1 and D2 are read in two times, and there is no packet contamination and unpacking
2. In one reading, the packet of D1D2 combined with two data packets is read, and packet contamination occurs
3. It is read twice. Part of the data of D1 and D2 is read for the first time, and the rest of the data of D2 is read for the second time, resulting in unpacking
4. It is read in two times. For the first time, some data of D1 is read, and for the second time, the remaining data of D1 and all data of D2 are read, resulting in unpacking
4.7.2 solutions
Idea: control the length of the content read by the receiver to solve the problem
Solution: solve the problem of unpacking and contamination through user-defined parsing + codec