Getting Started with Netty


1. NIO

  The bottom layer of the Netty framework is a high degree of encapsulation of NIO, so before you want to learn Netty better, you should first understand what NIO is - NIO is the abbreviation of non-blocking,
the new api provided in jdk1.4, his other Features are as follows:
  * Provide (Buffer) buffer support for all primitive types, character set encoding and decoding solutions.
  * Channel : a new primitive I/O abstraction. A file access interface that supports locks and memory-mapped files. Provides multi-channel (non-bloking) non-blocking high-
scalability network I/O.
  NIO is a non-blocking I/O. It handles all IO events by a dedicated thread and is responsible for distribution, and it only triggers when the event arrives,
rather than synchronously monitoring events; Communication through wait, notify, etc. Make sure every context switch is meaningful. Reduce unnecessary thread switching.
  The biggest difference between NIO and IO is the way data is packaged and transmitted. IO processes data in a stream, while NIO processes data in blocks. The core part of NIO consists of
three parts: Channels, Buffers, and Selectors.

(1) Channel and Buffer

  Under normal circumstances, all IOs in NIO start from a Channel. Channels are a bit like streams. Data can be read from Channel to Buffer, or
written from Buffer to Channel. Implementation of some main Channels in JAVA NIO: FileChannel, DatagramChannel, SocketChannel,
ServerSocketChannel. These implementation classes cover UDP and TCP network IO, as well as file IO.
  Some implementation classes of Buffer: ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer, cover
the basic data types that can be sent through IO: byte, short, int, long, float, double and char.

(2) Selector

  Selector allows a single thread to process multiple Channels.
Using Selector is handy if your app has multiple connections (channels) open, but each connection has low traffic . To use Selector, you have to register Channel with Selector, and then call its select() method. This method will block until a registered
channel has an event ready. Once this method returns, the thread can handle these events, such as new connection coming in, data receiving, etc.

2. Netty

(1) Getting started with Netty

  For big data, Internet projects with high access scenarios, or the collaborative work of multiple systems, one server is simply not competent. It is necessary to split the system into multiple services and
deploy them on multiple machines as needed. These services are very flexible and can expand elastically with the amount of access. But the cross-service communication of multiple modules is a huge
waste of time and resources. Traditional Blocking IO cannot be solved because there will be thread blocking problems, and using non-blocking IO (NIO) requires too much energy. The Netty framework
(RPC framework) solves this problem very well.
  Netty is a java open source framework provided by JBOSS. Netty provides an asynchronous, event-driven network application framework and tools for rapid development of high-performance,
high-reliability network server and client programs. He is a program that encapsulates java socket noi, we can just use it directly.
  The steps of the Netty communication server:
    1. Create two NIO thread groups, one for network event processing (accepting client connections), and the other for reading and writing network communication.
    2. Create a ServerBootstrap object and configure a series of parameters of Netty, such as the cache size for accepting outgoing data.
    3. Create a class ChannelInitializer for actually processing data, and perform initialization preparations, such as setting the character set,
format and interface for actually processing data that accept outgoing data.
    4. Bind the port, execute the synchronous blocking method and wait for the server to start.
    5. Close the corresponding resources.

Server chestnut:
  Server manager:

/**
* The server handles the channel. This is just to print the content of the request, and does not respond to the request.
* Inherited from ChannelHandlerAdapter, this class implements the ChannelHandler interface,
* ChannelHandler provides a number of event handling interface methods, which you can then override.
* @author lcy
*
*/
public class DiscartServiceHandler extends ChannelHandlerAdapter {
/**
* This method will be called when the client receives a new message
*
* @param ctx
* Context information for channel processing
* @param msg
* Received message
*/
@Override
public  void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
 try {
 // Convert the received information into a buffer 
ByteBuf str = (ByteBuf) msg;
 // Print the transmitted information 
System.out.print(str.toString (CharsetUtil.UTF_8));
} finally {
 // Release the ByteBuf object 
ReferenceCountUtil.release(msg);
}
}

/**
* Triggered on exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//输出错误信息
cause.printStackTrace();
ctx.close();
}
}

  Server:

/**
* Server
* @author lcy
*
*/
public class DiscartServer {
private int port;

public DiscartServer(int port) {
super();
this.port = port;
}

public  void run() throws Exception {
 // (1) Set up two thread groups
 // used to receive incoming connections 
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
 // used to process accepted connections 
NioEventLoopGroup workGroup = new NioEventLoopGroup() ;
System.out.println( "Port ready to run" + port);
 try {
 // (2) Auxiliary tool class, a series of configurations for server channels 
ServerBootstrap bootstrap = new ServerBootstrap();
 // (3) Binding Two thread groups
 // Set the group, this step is necessary, if the group is not set, it will report java.lang.IllegalStateException: group not set exception 
bootstrap = bootstrap.group(bossGroup, workGroup);
 // (4) Specify NIO pattern 
/** *
* ServerSocketChannel is implemented based on NIO's selector to receive new connections
* Here tells Channel how to get new connections.
*/ 
bootstrap = bootstrap.channel(NioServerSocketChannel.class ) ;
 // (5) To configure the specific data processing method, add the rule 
bootstrap = bootstrap.childHandler( new ChannelInitializer<SocketChannel> () {

@Override
protected  void initChannel(SocketChannel arg0) throws Exception {
 // Disconnect from the client that has not communicated with the server within 50 seconds 
arg0.pipeline().addLast( new ReadTimeoutHandler(50 ));
arg0.pipeline().addLast( new HttpObjectAggregator(1048576 ));
 // Add the class that actually handles data 
arg0.pipeline().addLast( new DiscarServiceHandler());
}
});
// (6) Set the TCP buffer 
bootstrap = bootstrap.option(ChannelOption.SO_BACKLOG, 128 );
 // Keep connected 
bootstrap = bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true );
 // (7) Bind the port and start receiving Incoming connection 
ChannelFuture sync = bootstrap.bind(port).sync();
 // (8) This will wait until the socket is closed 
sync.channel().closeFuture().sync();
} finally {
 // (9) Close the resource 
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
//服务开启
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new DiscartServer(port).run();
System.out.println("server:run()");
}
}

Client chestnuts:

  The class that actually handles the data:

public class ChannelClient extends ChannelInitializer{

@Override
protected  void initChannel(Channel arg0) throws Exception {
 // Disconnect from the client that has not communicated with the server within 50 seconds 
arg0.pipeline().addLast( new ReadTimeoutHandler(50 ));
arg0.pipeline().addLast(new HttpObjectAggregator(1048576));
//设置Channel
arg0.pipeline().addLast(new ChannelHandlerAdapter(){

@Override
public  void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
 try {
 // Convert the received information into a buffer 
ByteBuf str = (ByteBuf) msg;
 // Print the transmitted information 
System.out.print(str.toString (CharsetUtil.UTF_8));
} finally {
 // Release the ByteBuf object 
ReferenceCountUtil.release(msg);
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//输出错误信息
cause.printStackTrace();
ctx.close();
}
});

}
}

  Client:

/**
* Client
* @author lcy
*/
public class Client {
@SuppressWarnings( "resource" )
 public  static  void main(String[] args) throws Exception {
 // Create a new thread group 
NioEventLoopGroup workGroup = new NioEventLoopGroup();
 // Initialize Netty 
Bootstrap bootstrap = new Bootstrap();
 // Specify the thread group of the work 
bootstrap = bootstrap.group(workGroup);
 // Specify the type of Channel. Since it is a client, NioSocketChannel is used. bootstrap.channel 
(NioSocketChannel.class );
 /**
* Set some properties of the link
*/ 
// Lower latency, disable disable nagle algorithm. The nagle algorithm is affected by TCP delayed acknowledgment, which results in sending request packets to the connection twice in succession. 
bootstrap.option(ChannelOption.TCP_NODELAY, true );
 // Keep connected to detect whether the other host crashes, avoid (server) forever blocking the input of TCP connection 
bootstrap.option(ChannelOption.SO_KEEPALIVE, true );
 // Use netty default decoding If the read is incomplete, the channelRead method will not be executed. Setting this property is a pity to ensure that Netty reads the complete 
bootstrap.option(ChannelOption.MAX_MESSAGES_PER_READ, Integer.MAX_VALUE);
 // Set the data processor 
bootstrap.handler( new ChannelClient());
 // Synchronous link 
Channel channel = bootstrap.connect ("127.0.0.1", 8080 ).sync().channel();
channel.writeAndFlush(Unpooled.copiedBuffer("Hello Netty...".getBytes()));
channel.closeFuture().sync();
workGroup.shutdownGracefully();
}
}

(2) Netty's data communication:

  1. Use the long-connection channel to communicate without interruption, that is, the channel between the server and the client is always open. If the server performance is good enough
and the number of clients is relatively large, this method is recommended.
  2. Submit data in batches at one time, using short connection mode. That is to say, first save the data to the local temporary buffer area or temporary table, and perform
one-time , or submit it by polling according to the scheduled task.
  3. Using a special long connection, within a specified period of time, if the server does not have any communication with a client, the connection will be disconnected. The next connection is when the client
sends a request to the server, and the connection is established again.

(3) Netty's codec:  

    1. Decoder The decoder is responsible for converting the message from bytes or other serial forms into the specified message object.
  2. Encoder The encoder converts the message object into bytes or other serial forms and transmits it on the network.
  Inbound "ByteBuf reads bytes and is decoded by ToIntegerDecoder, then stores the decoded message in the List collection, and then passes it to
the next ChannelInboundHandler in ChannelPipeline.
  Decoder:
    1) ByteToMessageDecoder, you need to judge whether the ByteBuf is read before There are enough bytes, otherwise the phenomenon of packet sticking will occur.
    2) ReplayingDecoder, you don't need to check the byte length yourself, but it has limitations:
      * Not all operations are supported by ByteBuf, if you call an unsupported operation, it will be Throws DecoderException.
      * ByteBuf.readableBytes() will not return the expected value most of the time.
    3) MessageToMessageDecoder (message-to-message)
  decoder is used to process inbound data, Netty provides many decoder implementations, which can be customized according to requirements Learn more.
  Encoder:
    1) MessageToByteEncoder
    2) MessageToMessageEncoder When you need to encode a message into other messages, you can use the MessageToMessageEncoder abstract class provided by Netty
to implement it. For example, encode an Integer into a String.

(4) Solve the problem of TCP sticking/unpacking in Netty

  If you want to solve the problem of sticking/unpacking of TCP, you must first know what is sticking and unpacking of TCP :
    TCP is a "stream" protocol, and the so-called stream is genetic data without boundaries. You can imagine that if the river water is like data, they are connected together, there is no
dividing line, the bottom layer of TCP does not understand the specific meaning of the upper layer business data, it will divide the packets according to the specific conditions of the TCP buffer, that is It is said that in the business, a complete
packet may be divided into multiple packets by TCP for sending, or multiple small packets may be encapsulated into a large data packet and sent out, which is the so-called sticky packet/unpacking problem.
  Solution:
  1. The message length is fixed. For example, the size of each message is fixed at 200 bytes. If it is not enough, fill the blanks with blanks.
  2. Add special characters at the end of the package for segmentation, such as adding a carriage return.
  3. Divide the message into a message header and a message body, and include a field representing the total length of the message in the message header, and then process the business logic.
  The method of solving TCP sticking/unpacking in Netty:
    1. Delimiter class: DelimiterBasedFrameDecoder (custom delimiter)
    2. Fixed length: FixedLengthFrameDecoder

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325540246&siteId=291194637