Netty series: overview of netty architecture

flydean
中文

Introduction

Why is Netty so good, and what improvements has it made on the basis of the NIO of the JDK itself? What is its structure and workflow? Please walk into today's netty series of articles: an overview of netty architecture.

netty architecture diagram

The main function of netty is to provide a simple NIO framework that can be combined with various upper-layer protocols to finally realize a high-performance server. The following is the architecture diagram provided by the netty official website:

From the above figure, we can see that the core of netty is mainly divided into three parts, which are an extensible event model, a unified API, and a powerful Byte Buffer. These three characteristics are the magic weapon for netty.

The following will explain the characteristics of netty in detail from these aspects, so that readers must understand the excellence of netty.

Rich Buffer data organization

First, starting from the bottom layer of the Buffer data structure, netty provides an io.netty.buffer package, which defines various types of ByteBuf and its derived types.

The basis of netty Buffer is the ByteBuf class, which is an abstract class. Other Buffer classes are basically derived from this class. This class also defines the tone of netty's overall Buffer.

The purpose of netty rewriting ByteBuf is to make the lowest ByteBuf faster and more suitable for expansion than the one that comes with the JDK. Specifically, netty's ByteBuf is faster than the ByteBuffer in the JDK, and at the same time, it is easier to extend. You can customize Buf according to your needs. In addition, netty has some built-in composite buffer types, so transparent zero copy can be achieved. For the dynamic buffer type, it can be expanded on demand like StringBuffer, which is very easy to use.

Zero copy

What is zero copy? Zero copy means not to make a copy when it needs to be copied. We know that data will be encapsulated into individual packets for transmission during transmission using the underlying protocol. When the transmitted data is too large to fit a packet, the data needs to be split. After the destination party receives the data, it needs to assemble the received data. Under normal circumstances, this assembly operation is for the data. Copy, copy the split object to a long data space.

For example, as shown in the following example, the bottom TCP packet combination is called the top HTTP packet, but it is not copied:

How to copy it? In the last article, we know that netty provides a tool class method Unpooled. There are many methods at the beginning of the wrapped class in this tool class. Let's give a few examples:

 public static ByteBuf wrappedBuffer(byte[]... arrays) {
        return wrappedBuffer(arrays.length, arrays);
    }

public static ByteBuf wrappedBuffer(ByteBuf... buffers) {
        return wrappedBuffer(buffers.length, buffers);
    }

public static ByteBuf wrappedBuffer(ByteBuffer... buffers) {
        return wrappedBuffer(buffers.length, buffers);
    }

The above three methods are encapsulating byte array, encapsulating ByteBuf and encapsulating ByteBuffer. These methods are all zero copy. You can choose by yourself according to the actual situation in the actual project.

Unified API

Generally speaking, in the traditional JDK IO API, depending on the transmission type or protocol, the API used is also different. We need to develop different applications for different transmission methods, which cannot be unified. As a result, the migration cannot be smooth, and additional processing is required when the program is expanded.

What is the transmission method? This refers to the way in which IO is implemented. For example, traditional blocking IO can be called OIO, Java's new IO can be called NIO, asynchronous IO can be called AIO, and so on.

And the traditional IO and NIO in the JDK are separated. If you use traditional IO at the beginning, when the number of your customers grows to a certain level and you are ready to switch to NIO, you will find that the switch is extremely complicated, because They are separated.

To solve this problem, netty provides a unified class Channel to provide a unified API.

First look at the methods defined in Channel:

From the above figure, we can see that using Channel can determine the current state of the channel, configure its parameters, and perform I/O operations on it, and the ChannelPipeline related to the channel is used to process the IO requests and events associated with the channel.

Using Channel can provide good support for NIO's TCP/IP, OIO's TCP/IP, OIO's UDP/IP and local transmission.

The switching of the transmission mode requires only a small cost replacement.

Of course, if you are not satisfied with the existing implementation, you can also customize the core API.

Event driven

Netty is an event-driven framework. The foundation of the event-driven framework is the event model. Netty specifically defines a very effective event model for IO. You can implement your own event types without breaking existing code. The custom event types in netty are distinguished from other event types through a strict type hierarchy, so they are highly extensible.

Event-driven in netty is the result of the joint action of ChannelEvent, ChannelHandler and ChannelPipeline. Among them, ChannelEvent represents the event that occurred, ChannelHandler defines how to handle the event, and ChannelPipeline is similar to an interceptor, allowing users to control the defined ChannelHandler by themselves, so as to achieve the result of controlling the event processing.

We look at a simple custom Handler:

public class MyHandler extends SimpleChannelInboundHandler<Object> {

    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 对消息进行处理
        ByteBuf in = (ByteBuf) msg;
        try {
            log.info("收到消息:{}",in.toString(io.netty.util.CharsetUtil.US_ASCII));
        }finally {
            ReferenceCountUtil.release(msg);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        //异常处理
        cause.printStackTrace();
        ctx.close();
    }
}

In the above example, we have defined how to handle received messages and exceptions. In subsequent articles, we will introduce the interaction between ChannelEvent, ChannelHandler and ChannelPipeline in detail.

Other excellent features

In addition to the three core features mentioned above, netty has several other advantages to facilitate the development of programmers.

For example, the support for SSL/TLS, the implementation of HTTP protocol, the implementation of WebSockets and the implementation of Google Protocol Buffers, etc., show that netty has strong application capabilities in various aspects and multiple scenarios.

Summarize

Netty is composed of three core components: buffer, channel, and event model. By understanding how these three core components work with each other, it is not difficult to understand the advanced functions built on top of netty.

For the examples in this article, please refer to: learn-netty4

This article has been included in http://www.flydean.com/03-netty-architecture/

The most popular interpretation, the most profound dry goods, the most concise tutorial, and many tips you don't know are waiting for you to discover!

Welcome to pay attention to my official account: "Program those things", know technology, know you better!

阅读 586

程序那些事
Spring,区块链,密码学,分布式,多线程等教程 欢迎关注我的公众号:程序那些事,更多精彩等着您!

欢迎访问我的个人网站:www.flydean.com

747 声望
409 粉丝
0 条评论

欢迎访问我的个人网站:www.flydean.com

747 声望
409 粉丝
文章目录
宣传栏