Seven Seven Eight Eight of Netty

    Netty is widely used in various scenarios, such as remote communication of Dubbo service, shuffle process of Hadoop, client and server communication in the game field, etc. Netty can easily define various private protocol stacks and is a powerful tool for network programming. Netty is an encapsulation of NIO. Netty does not encapsulate AIO because Linux's AIO is also implemented by epoll, and the performance is not greatly improved, and Netty's reactor model is not suitable for encapsulating AIO, so Netty gave up support for AIO. With the rise of the Internet of Things, a large number of devices need to be interconnected, and Netty must be a powerful tool.

    In the traditional NIO programming model, the poller selector needs to be used to poll each channel for read and write events, and the ByteBuffer api is obscure and difficult to understand, it is very complicated to maintain, and the business is difficult to decouple. Netty helps us shield NIO's details, and do a lot of performance optimization. Let's take a look at some of the details in Netty.

 Note: In the following example, the netty version number is 4.1.52.Final, and the maven configuration is as follows:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>org.netty</groupId>
	<artifactId>netty-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>netty-demo</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-all</artifactId>
			<version>4.1.52.Final</version>
		</dependency>
	</dependencies>
</project>

First, the startup of the client side and the server side

    1. Server side

    The group method in ServerBootstrap has two input parameters, NioEventLoopGroup, which are two thread pools, which respectively represent the thread that accepts the request and the thread that processes the request. This is the embodiment of the reactor model. When each client connects, the server will have A channel corresponds to it, and the childHandler method is to bind a bunch of processors to the channel, where three inbound processors are bound. ctx.fireChannelRead(msg) indicates that the lower-level processor is notified for processing. If this method is not called, the read event will terminate and propagate to the downstream processor.

public class NettyDemoServer {

	public static void main(String[] args) {
		ServerBootstrap serverBootstrap = new ServerBootstrap();
		serverBootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup()).channel(NioServerSocketChannel.class)
				.childHandler(new ChannelInitializer<NioSocketChannel>() {
					@Override
					protected void initChannel(NioSocketChannel ch) throws Exception {
						ch.pipeline().addLast(new StringDecoder()).addLast(new SimpleChannelInboundHandler<Object>() {
							@Override
							protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
								System.out.println("Handler1:" + msg);
								ctx.fireChannelRead(msg);
							}
						}).addLast(new SimpleChannelInboundHandler<Object>() {
							@Override
							protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
								System.out.println("Handler2:" + msg);
								ctx.fireChannelRead(msg);
							}

						});
					}
				}).bind(8080).addListener(o -> {
					if(o.isSuccess()){
						System.out.println("启动成功");
					}
				});
	}
}

    2. Client side

    The client encodes as a string and writes a piece of data to the server every three seconds

public class NettyDemoClient {

	public static void main(String[] args)  {
		Bootstrap bootstrap = new Bootstrap();
		NioEventLoopGroup group = new NioEventLoopGroup();
		bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<Channel>() {
			@Override
			protected void initChannel(Channel ch) {
				ch.pipeline().addLast(new StringEncoder());
			}
		});
		Channel channel = bootstrap.connect("localhost", 8080).channel();
		while (true) {
			channel.writeAndFlush(new Date().toLocaleString() + ":测试netty");
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

    The operation effect is as follows:

启动成功
Handler1:2020-9-11 17:23:19:测试netty
Handler2:2020-9-11 17:23:19:测试netty
Handler1:2020-9-11 17:23:23:测试netty
Handler2:2020-9-11 17:23:23:测试netty
Handler1:2020-9-11 17:23:26:测试netty
Handler2:2020-9-11 17:23:26:测试netty

    Netty shields all IO processing details, and only needs to define the corresponding business without inbound and outbound processors during business development, realizing the decoupling of business and communication.

Second, the data processing channel pipeline and channel processor channelHandler

    1、channelHandler

   Inherited from ChannelHandler, there are two major interfaces, ChannelInboundHandler and ChannelOutBoundHandler, which represent the inbound and outbound interfaces respectively. For the station handler, the channelRead(ChannelHandlerContext ctx, Object msg) method will be triggered when a message comes in. For the outbound handler, write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) will be triggered when data is written out. ChannelInboundHandlerAdapter and ChanneloutBoundHandlerAdapter are general implementations of the two types of interfaces. There are some very simple implementations in it, just pass the read and write events in the pipeline.

    2. The event propagation sequence of the pipeline

   For inbound handlers, the order of execution remains the same as the order in which addLast was added, which has been verified in the example printed earlier, and for outbound handlers, the order of execution is reversed from the order in which addLast was added. Let's verify this on the client side by adding two outbound handlers on the client side.

public class NettyDemoClient {

	public static void main(String[] args) {
		Bootstrap bootstrap = new Bootstrap();
		NioEventLoopGroup group = new NioEventLoopGroup();
		bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<Channel>() {
			@Override
			protected void initChannel(Channel ch) {
				ch.pipeline().addLast(new StringEncoder()).addLast(new OutBoundHandlerFirst()).addLast(new OutBoundHandlerSecond());
			}
		});
		Channel channel = bootstrap.connect("localhost", 8080).channel();
		while (true) {
			channel.writeAndFlush(new Date().toLocaleString() + ":测试netty");
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public static class OutBoundHandlerFirst extends ChannelOutboundHandlerAdapter {
		@Override
		public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
			System.out.println("OutBoundHandlerFirst");
			super.write(ctx, msg, promise);
		}
	}

	public static class OutBoundHandlerSecond extends ChannelOutboundHandlerAdapter {
		@Override
		public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
			System.out.println("OutBoundHandlerSecond");
			super.write(ctx, msg, promise);
		}
	}
}

    The print log is as follows:

OutBoundHandlerSecond
OutBoundHandlerFirst
OutBoundHandlerSecond
OutBoundHandlerFirst
OutBoundHandlerSecond
OutBoundHandlerFirst
OutBoundHandlerSecond
OutBoundHandlerFirst
OutBoundHandlerSecond
OutBoundHandlerFirst

    Here, the outbound handler is verified, and the order of execution is reversed from the order added by addLast.

    The pipeline actually maintains a doubly linked list. Why does this phenomenon occur? We found the answer in AbstractChannelHandlerContext, the findContextInbound method is to take the next node, and the findContextOutbound method is to take the previous node

 private AbstractChannelHandlerContext findContextInbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.next;
        } while (!ctx.inbound);
        return ctx;
    }

    private AbstractChannelHandlerContext findContextOutbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }

Third, the life cycle of the processor

    When the connection is established, handlerAdded->channelRegistered->channelActive->channelRead->channelReadComplete

    When the connection is closed, channelInactive->channelUnregistered->handlerRemoved

    Description: channelRead and channelReadComplete are called every time a complete packet is read

Fourth, unpacking and sticking to solve

    The TCP protocol is a streaming protocol, so when transmitting data, a complete packet will not be transmitted according to our business, and multiple packets may be sent together. A complete packet is split into multiple small packets for transmission. At this time, the receiving end needs to combine multiple packets into a complete packet for parsing. So what solutions does netty have?

1. FixedLengthFrameDecoder     , a fixed-length unpacker

    Each data packet has a fixed length, for example, each data packet is 50, suitable for simple scenarios

    2. Line unpacker LineBasedFrameDecoder

    Unpack with newlines

    3. DelimiterBasedFrameDecoder DelimiterBasedFrameDecoder

    This is similar to the separator unpacker, except that special symbols can be customized for unpacking, such as # @ and other symbols. When using, you must ensure that these special symbols are not included in the formal message.

    4, the length field unpacker LengthFieldBasedFrameDecoder

    This is the most common unpacker, and almost all binary custom protocols can be unpacked based on this unpacker, as long as a length field is defined in the protocol header, for example, 4 bytes are used to store messages body length

 

{{o.name}}
{{m.name}}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324039015&siteId=291194637