NIO synchronous and asynchronous blocking -- transition to netty (an asynchronous event driven network application framework)

BIO and NIO

IO is in the form of synchronous blocking and NIO is in the form of synchronous non blocking. NIO does not realize asynchronous. After JDK 1.7, NIO library package is upgraded to support asynchronous non blocking
Schoolmate model NIO2.0(AIO)
BIO: synchronous blocking IO. The server implementation mode is one connection and one thread. 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. Of course, it can be improved through the thread pool mechanism.
NIO: synchronous non blocking IO. The implementation mode of the server is one request and one thread. That is to say, the connection requests sent by the client will be registered on the multiplexer. Only when the multiplexer polls that there is an I/O request connected can a thread be started for processing.
AIO(NIO.2): asynchronous non blocking IO. The server implementation mode is an effective request for a thread. The I/O requests of the client are completed by the OS first, and then the server application is notified to start the thread for processing.

During synchronization, the application will directly participate in IO read and write operations, and our application will directly block to a certain method until the data is ready:
Or use the strategy of rotation training to check the ready state of data in real time, if ready, get the data
In asynchronous mode, all IO read and write operations are handed over to the operating system, which has no direct relationship with our application program. Our program does not need to have any relationship with IO read and write operations
When the system completes the IO read-write operation, it will send a notice to our application, and our application can take the data pole directly.

Pseudo asynchronous

Since a client of BIO needs a thread to process, we optimize it. The backend uses thread pool to process the request access of multiple clients, forming a proportion relationship between the number of clients M: the maximum number of threads in the thread pool n, where M can be far greater than N. through the thread pool, we can flexibly allocate thread resources, set the maximum value of threads, and prevent concurrent connection due to large amount of threads In causes the thread to run out.
Principle:
When a new client is accessed, the Socket of the client is encapsulated into a Task (the Task implements the Runnable interface of java) and delivered to the back-end thread pool for processing. Because the thread pool can set the size of the message queue and the maximum value of the thread pool, its resource occupation is controllable. No matter how many clients access concurrently, it will not lead to resource occupation Exhaustion or downtime of the source.

IO model relationships

What is blocking

Blocking concept: when an application obtains network data, if the network transmission is slow, the program will wait until the transmission is completed.

What is non blocking

The application can get the prepared data directly without waiting
IO is synchronous blocking and NIO is synchronous non blocking. NIO does not implement asynchrony. After JDK 1.7, NIO library package was upgraded
, supporting asynchronous fee blocking communication model NIO2.0(AIO)

Select KEY

1,SelectionKey.OP_CONNECT
2,SelectionKey.OP_ACCEPT
3,SelectionKey.OP_READ
4,SelectionKey.OP_WRITE
If you are interested in more than one event, you can use the bitwise OR operator to join constants as follows:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
In the source code of the SelectionKey class, we can see the following four properties. Four variables are used to represent four different types of events: readable, writable, connectable, and acceptable

Example

Non blocking:

########Client

public static void main(String[] args) throws IOException {
			System.out.println("Client started:");
			//Create socket channel
			SocketChannel socketChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1",8080));
			//Switch asynchronous non blocking
			socketChannel.configureBlocking(false);
			//Specify cache size
			ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
			byteBuffer.put(new Date().toString().getBytes());
			//Switch to read mode
			byteBuffer.flip();
			socketChannel.write(byteBuffer);
			byteBuffer.clear();
			socketChannel.close();
		}

#######Server

public static void main(String[] ages) throws IOException {
	System.out.println("Server start..........");
	//Create service channel
			ServerSocketChannel open = ServerSocketChannel.open();
			//Asynchronous non blocking
			open.configureBlocking(false);
			//Binding connection
			open.bind(new InetSocketAddress(8080));
			//Get selector
			Selector selector = Selector.open();
			//Register channel to selector
			open.register(selector, SelectionKey.OP_ACCEPT);
			while(selector.select()>0) {
				//Get the current selector. There are registered events that have been monitored
				Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
				//Judge event readiness
				while(iterator.hasNext()) {
					//Get ready to continue events
					SelectionKey next = iterator.next();
					//Determine if the event is ready (not ready)
					if(next.isAcceptable()) {
						//Get client connection
						SocketChannel accept = open.accept();
						//Set blocking event
						accept.configureBlocking(false);
						//Register the channel to the selector
						accept.register(selector, SelectionKey.OP_READ);
					}else if(next.isReadable()) {//Ready
						//Get channel in current state
						SocketChannel channel = (SocketChannel) next.channel();
						//Read data
						int len=0;
						ByteBuffer allocate = ByteBuffer.allocate(1024);
						while((len=(channel.read(allocate)))>0) {
							allocate.flip();
							System.out.println(new String(allocate.array(),0,len));
							allocate.clear();
						}
						
					}
					iterator.remove();
				}
			}
}

Why netty

In this section, we summarize the reasons why developers are not recommended to directly use the NIO class library of JDK for development:

  1.  NIO's class library and API are complex and troublesome to use. You need to be familiar with Selector, ServerSocketChannel, SocketChannel, ByteBuffer, etc;
    
  2.  You need to have other additional skills to pave the way, for example, be familiar with Java multithreading programming, because NIO programming involves Reactor mode, you must be very familiar with multithreading and network programming to write high-quality NIO programs;
    
  3.  It is very difficult to make up the reliability. For example, the client is faced with disconnection and reconnection, network flash, half packet read and write, failure cache, network congestion and abnormal code stream processing, etc. NIO programming is characterized by relatively easy function development, but the workload and difficulty of reliability compensation are very large;
    
  4.  The bug of JDK NIO, such as the infamous epoll bug, will lead to null polling of the Selector and eventually 100% CPU. The official claims that the problem was fixed in update 18 of JDK 1.6, but it still exists until JDK 1.7, except that the probability of occurrence of the bug has decreased a little, and it has not been fundamentally resolved. The bug and the list of questions related to the bug are as follows:
    

Netty application scenario

1. In the distributed open source framework, the underlying rpc communication of dubbo, Zookeeper and RocketMQ is netty.
2. In game development, the bottom layer uses netty communication.

Single client connection:

Server:

public static void main(String[] args) {
        startServer();
    }

    public static void startServer(){
        //1. Define the server startup class
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        //2. Define workgroup: boss distributes requests to each worker:boss is responsible for listening to port requests, and worker is responsible for processing requests (read and write)
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();

        //3. Definition working group
        serverBootstrap.group(boss,worker);

        //4. Set channel
        serverBootstrap.channel(NioServerSocketChannel.class);//A
        //Serverbootstrap.channelfactory (new reflective channelfactory (nioserversocketchannel. Class)); / / the writing method of the old version, but this process has the same process in A

        //5. Add handler, the processor in the pipeline, and construct it through ChannelInitializer
      serverBootstrap.childHandler(new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel channel) throws Exception {
                //This method is called every time the client connects. It is the method initialized for the channel

                //Get the pipeline chain (execution chain, handler chain) in channel channel
                ChannelPipeline pipeline = channel.pipeline();
                pipeline.addLast(new StringDecoder());
                pipeline.addLast("serverHandler1",new ServerHandler());
                pipeline.addLast("serverHandler2",new ServerHandler2());
                pipeline.addLast(new StringEncoder());

                System.out.println("success to initHandler!");
            }
        });

        //6. Set parameters
        //Setting parameters, TCP parameters
        serverBootstrap.option(ChannelOption.SO_BACKLOG, 2048);         //Size of connection buffer pool
        serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);//Keep links active, clear dead links
        serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);//Turn off delay sending

        //7. Binding ip and port
        try {
            ChannelFuture channelFuture = serverBootstrap.bind("127.0.0.1", 8080).sync();//channel object of Future mode
            //7.5. Monitor off
            channelFuture.channel().closeFuture().sync();  //Wait for the service to shut down, after which resources should be released
        } catch (InterruptedException e) {
            System.out.println("server start got exception!");
            e.printStackTrace();
        }finally {
            //8. Close resources gracefully
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }

    }
handle:
      public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
	super.channelRead(ctx, msg);
	System.out.println("Server message:"+msg.toString());
	ctx.channel().writeAndFlush("serverHandler"+System.currentTimeMillis());
	//Send the message to the next Handler
    ctx.fireChannelRead(msg);
}

}
public class ServerHandler2 extends ChannelInboundHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
super.channelRead(ctx, msg);
System.out.println("server 2 message" + msg);
ctx.channel().writeAndFlush("this is ServerHandler2"+System.currentTimeMillis());
}
}

Client

public class NettySingleClient {
public static void main(String[] args) {
    startClient();
}

public static void startClient(){
    //1. Define service class
    Bootstrap clientBootstap = new Bootstrap();

    //2. Define execution thread group
    EventLoopGroup worker = new NioEventLoopGroup();

    //3. Set thread pool
    clientBootstap.group(worker);

    //4. Set channel
    clientBootstap.channel(NioSocketChannel.class);

    //5. Add Handler
    clientBootstap.handler(new ChannelInitializer<Channel>() {
        @Override
        protected void initChannel(Channel channel) throws Exception {
            System.out.println("Client initialization:");
            ChannelPipeline pipeline = channel.pipeline();
            pipeline.addLast("StringDecoder",new StringDecoder());
            pipeline.addLast("StringEncoder",new StringEncoder());
            pipeline.addLast("ClientHandler",new ClientHandler());
        }
    });

    //6. Establish connection
    ChannelFuture channelFuture = clientBootstap.connect("127.0.0.1",8080);
    try {
        //7. Test input
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        while(true){
            System.out.println("Please input:");
            String msg = bufferedReader.readLine();
            channelFuture.channel().writeAndFlush(msg);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        //8. Close connection
        worker.shutdownGracefully();
    }
}

}
handle:

public class ClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    System.out.println("client receive msg:"+msg.toString());
}

}

Published 20 original articles, won praise and 361 visitors
Private letter follow

Tags: JDK network Netty Programming

Posted on Thu, 16 Jan 2020 08:43:13 -0500 by JennyG