Deep Analysis of Netty 10: Application of Netty Design Patterns

Keywords: Java

10 Design Patterns

10.1 Singleton Model

Three principles: global only one instance (private construction method), static, avoid multi-threaded synchronized creation (static, synchronized)

Examples: ReadTimeoutException, MqttEncoder

ReadTimeoutException:

public final class ReadTimeoutException extends TimeoutException {

    private static final long serialVersionUID = 169287984113283421L;

    public static final ReadTimeoutException INSTANCE = new ReadTimeoutException();

    private ReadTimeoutException() { }
}

The same is true of MqttEncoder.
Because this is a singleton, add @Sharable

@Sharable: https://blog.csdn.net/zhailuxu/article/details/83472632

10.2 Strategy Model

Encapsulating a series of replaceable algorithm families to support dynamic selection of a strategy

Default Event Executor Chooser Factory # new Chooser implements the policy pattern and chooses different policies according to the length of the NioEventLoop array

public EventExecutorChooser newChooser(EventExecutor[] executors) {
    if (isPowerOfTwo(executors.length)) {
        return new PowerOfTowEventExecutorChooser(executors);
    } else {
        return new GenericEventExecutorChooser(executors);
    }
}

10.3 Decorator Model

The decorator and the decorated inherit the same interface (because it is to strengthen the same method), and the decorator dynamically modifies the decorated's behavior (through combination).

WrappedByteBuf,UnReleaseableByteBuf,SimpleLeakAwareByteBuf

WrappedByteBuf inherits from ByteBuf and can be regarded as the same interface. There is a decorated ByteBuf inside. Most of the methods are delegated to the decorated ByteBuf.
Two subclasses of WrappedByteBuf, UnReleaseable ByteBuf and SimpleLeak AwareByteBuf, such as rewriting release methods (the former does not need to be released, and the latter adds memory leak detection)

class WrappedByteBuf extends ByteBuf {

    protected final ByteBuf buf;
}
final class UnreleasableByteBuf extends WrappedByteBuf {
    public boolean release() {
        return false;
    }
}
final class SimpleLeakAwareByteBuf extends WrappedByteBuf {
    @Override
    public boolean release(int decrement) {
        boolean deallocated = super.release(decrement);
        if (deallocated) {
            leak.close();
        }
        return deallocated;
    }
}

10.4 Observer Model

Observers and observers, observers subscribe to messages, observers publish messages, subscriptions can be received, cancelled subscriptions can not be received.

The writeAndFlush method is the implementation of this pattern:

public void write(NioSocketChannel channel, Object object) {
    ChannelFuture channelFuture = channel.writeAndFlush(object);//Create the Observed
    channelFuture.addListener(future -> {//AddiListener is adding observers.
        if (future.isSuccess()) {//When the writeAndFlush method is finished, it can be judged by isSuccess.

        } else {

        }
    });
    channelFuture.addListener(future -> {
        if (future.isSuccess()) {

        } else {

        }
    });
}

In the process of writing AndFlush, there is the following code, newPromise is to create the observer

public ChannelFuture writeAndFlush(Object msg) {
    return writeAndFlush(msg, newPromise());
}

Save the observer through an array

10.5 iterator mode

There is an iterator interface to access the various objects in the container

Implementation of Composite ByteBuf (zero copy):

Use code:

public static void main(String[] args) {
    ByteBuf header = Unpooled.wrappedBuffer(new byte[]{1, 2, 3});
    ByteBuf body = Unpooled.wrappedBuffer(new byte[]{4, 5, 6});
 
    ByteBuf merge = merge(header, body);
    merge.forEachByte(value -> {
        System.out.println(value);
        return true;
    });
}
 
public static ByteBuf merge(ByteBuf header, ByteBuf body) {
    CompositeByteBuf byteBuf = ByteBufAllocator.DEFAULT.compositeBuffer(2);
    byteBuf.addComponent(true, header);
    byteBuf.addComponent(true, body);
 
    return byteBuf;
}

This code adds two ByteBuf s together, and forEachByte implements the iterator pattern. So how do you say it's a zero copy?

The implementation of forEachByte is in AbstractByteBuf, which has such a piece of code:

@Override
public int forEachByte(ByteProcessor processor) {
    ensureAccessible();
    try {
        return forEachByteAsc0(readerIndex, writerIndex, processor);
    } catch (Exception e) {
        PlatformDependent.throwException(e);
        return -1;
    }
}

Start with reader Index and read to write Index:

private int forEachByteAsc0(int start, int end, ByteProcessor processor) throws Exception {
    for (; start < end; ++start) {
        if (!processor.process(_getByte(start))) {
            return start;
        }
    }
 
    return -1;
}

Look at the implementation of _getByte and find Composite ByteBuf's:

@Override
protected byte _getByte(int index) {
    Component c = findComponent(index);//Find the component corresponding to the index, namely the added header and body
    return c.buf.getByte(index - c.offset);
}

In fact, the ByteBuf to be merged is added to Composite ByteBuf.

If other classes iterate, all data will be copied, such as ByteBufAllocator.DEFAULT.ioBuffer()

10.6 Chain of Responsibility Model

Definition: Give multiple objects the opportunity to process requests, thus avoiding the coupling between the sender and the recipient of the request, linking the object into a chain and passing the request along the chain until one object processes it.

Many filter s in java, whether to continue to pass down, are implemented by return true or return method.

Four elements of the responsibility chain model:

1 Responsibility Processor Interface

2 Create chains, add and delete responsibility processor interfaces

3 Context

Because the responsible processor interface needs to be context-aware when dealing with events to get the objects it needs.

In addition

Chain termination mechanism.

channelHandler and Pipline constitute the responsibility chain model:

1.ChannelHandler is the responsible processor interface. ChannelInboundHandler and ChannelOuntboundHandler are its two enhancements.

2. Channel Pipeline is the creation of a chain, which defines methods to add and remove the interface of the responsible processor, such as add and remove.

3.ChannelHandlerContext is the context. When channelHandler is added to the chain, it will be encapsulated as ChannelHandlerContext.

public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker {
 	//Return the channel bound by this chain
    Channel channel();
    //Return the executor to perform the task, which is the NioEventLoop corresponding to channel
    EventExecutor executor();
    ...
}

4. Chain of Liability Termination Mechanism

Customize an InBoundHandlerC

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

The ctx.fiteChannelRead method is designed to pass on responsibility. If the comment is omitted, the message will not be delivered.
If you do not override the channelRead method, the ChannelInboundHandlerAdapter#channelRead method will also be passed by default:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    ctx.fireChannelRead(msg);
}

Finally, how does the message go down step by step? Look at the AbstractChannelHandlerContext#fireChannelRead method:

@Override
public ChannelHandlerContext fireChannelRead(final Object msg) {
    invokeChannelRead(findContextInbound(), msg);
    return this;
}

findContextInbound():

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

That is, it's pointing to the next ChannelHandlerContext object repeatedly.

Posted by jernhenrik on Fri, 06 Sep 2019 05:42:38 -0700