Make writing a habit together! This is my first day to participate in the “Gold Digging Day New Plan · April More text challenge”, click to see the details of the activity.

Introduction to the

In Netty we need to pass various types of messages. These messages can be strings, arrays, or custom objects. Different objects may need to be converted to each other, which requires a converter that can be freely converted. Netty provides a framework for converting messages to each other in order to unify coding rules and facilitate user expansion. This article will explain the implementation of this framework.

Introduction of framework

Netty provides the messages and the conversion between the three classes, these three classes are abstract classes, respectively is MessageToMessageDecoder, MessageToMessageEncoder and MessageToMessageCodec.

Let’s take a look at their definitions:

public abstract class MessageToMessageEncoder<I> extends ChannelOutboundHandlerAdapter
Copy the code
public abstract class MessageToMessageDecoder<I> extends ChannelInboundHandlerAdapter
Copy the code
public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN> extends ChannelDuplexHandler 
Copy the code

MessageToMessageEncoder inherited from ChannelOutboundHandlerAdapter, to write the message in the channel.

MessageToMessageDecoder inherited from ChannelInboundHandlerAdapter, is responsible for the read messages from the channel.

MessageToMessageCodec inherits from the ChannelDuplexHandler, which is a two-way handler that reads and writes messages to a channel.

With these three abstract classes, let’s look at the concrete implementation of these three classes.

MessageToMessageEncoder

MessageToMessageEncoder MessageToMessageEncoder MessageToMessageEncoder

public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { CodecOutputList out = null; try { if (acceptOutboundMessage(msg)) { out = CodecOutputList.newInstance(); @SuppressWarnings("unchecked") I cast = (I) msg; try { encode(ctx, cast, out); } finally { ReferenceCountUtil.release(cast); } if (out.isEmpty()) { throw new EncoderException( StringUtil.simpleClassName(this) + " must produce at least one message."); } } else { ctx.write(msg, promise); } } catch (EncoderException e) { throw e; } catch (Throwable t) { throw new EncoderException(t); } finally { if (out ! = null) { try { final int sizeMinusOne = out.size() - 1; if (sizeMinusOne == 0) { ctx.write(out.getUnsafe(0), promise); } else if (sizeMinusOne > 0) { if (promise == ctx.voidPromise()) { writeVoidPromise(ctx, out); } else { writePromiseCombiner(ctx, out, promise); } } } finally { out.recycle(); }}}}Copy the code

The write method takes a raw object, MSG, that needs to be converted, and a ChannelPromise that represents the channel’s read and write progress.

A type determination is made on the MSG, which is implemented in the acceptOutboundMessage.

    public boolean acceptOutboundMessage(Object msg) throws Exception {
        return matcher.match(msg);
    }
Copy the code

The matcher here is a TypeParameterMatcher object, which is a property initialized in the MessageToMessageEncoder constructor:

    protected MessageToMessageEncoder() {
        matcher = TypeParameterMatcher.find(this, MessageToMessageEncoder.class, "I");
    }
Copy the code

The “I” here is the MSG type to match.

If there is no match, ctx.write(MSG, promise) is continued; Writes the message, unconverted, to a channel for the next handler to call.

If the match is successful, the core encode method is called :encode(CTX, cast, out);

Note that the Encode method in MessageToMessageEncoder is an abstract method that needs to be extended by the user in an inherited class.

The encode method actually converts the MSG object into the object to be converted and then adds it to out. This out is a list object, specifically a CodecOutputList object. As a list, out is a list that can store multiple objects.

When is out written to a channel?

Don’t worry, there is a finally block at the end of the write method, in which out is written to a channel.

Since out is a List, it is possible to write the object part of out successfully, so special handling is required here.

Check whether there is only one object in out. If there is only one object, write it directly to channel. If there is more than one object in out, then there are two cases. The first case is if the promise passed in is a voidPromise, then the writeVoidPromise method is called.

What is voidPromise?

We know that a Promise has a variety of states, and we can know the data writing condition through the state change of the Promise. For voidPromise, it only cares about one failed state and none of the others.

If the user is concerned about other states of a promise, the writePromiseCombiner method is called to combine the states of multiple objects into a single promise return.

In fact, in writeVoidPromise and writePromiseCombiner, objects in out are taken out one by one and written into a channel, thus generating multiple promises and merging promises:

    private static void writeVoidPromise(ChannelHandlerContext ctx, CodecOutputList out) {
        final ChannelPromise voidPromise = ctx.voidPromise();
        for (int i = 0; i < out.size(); i++) {
            ctx.write(out.getUnsafe(i), voidPromise);
        }
    }

    private static void writePromiseCombiner(ChannelHandlerContext ctx, CodecOutputList out, ChannelPromise promise) {
        final PromiseCombiner combiner = new PromiseCombiner(ctx.executor());
        for (int i = 0; i < out.size(); i++) {
            combiner.add(ctx.write(out.getUnsafe(i)));
        }
        combiner.finish(promise);
    }
Copy the code

MessageToMessageDecoder

And encoder corresponding is decoder, MessageToMessageDecoder logic and MessageToMessageEncoder.

A TypeParameterMatcher object is also defined to detect the incoming message type:

    protected MessageToMessageDecoder() {
        matcher = TypeParameterMatcher.find(this, MessageToMessageDecoder.class, "I");
    }
Copy the code

An important method in decoder is the channelRead method. Let’s look at its implementation:

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { CodecOutputList out = CodecOutputList.newInstance(); try { if (acceptInboundMessage(msg)) { @SuppressWarnings("unchecked") I cast = (I) msg; try { decode(ctx, cast, out); } finally { ReferenceCountUtil.release(cast); } } else { out.add(msg); } } catch (DecoderException e) { throw e; } catch (Exception e) { throw new DecoderException(e); } finally { try { int size = out.size(); for (int i = 0; i < size; i++) { ctx.fireChannelRead(out.getUnsafe(i)); } } finally { out.recycle(); }}}Copy the code

The type of MSG is first detected, and only accepted types are decode, otherwise the MSG is added to the CodecOutputList.

Finally, pull out the out objects one by one in the finally code block and call ctx.FireChannelRead to read them.

The key method for message transformation is decode, which is also an abstract method that requires concrete functionality to be implemented in inherited classes.

MessageToMessageCodec

Earlier, I described an encoder and a decoder, both of which are unidirectional. The final coDEC is called MessageToMessageCodec, which is a two-way coDEC that can receive and send messages.

Let’s take a look at its definition:

public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN> extends ChannelDuplexHandler
Copy the code

MessageToMessageCodec inherits from ChannelDuplexHandler and receives two generic parameters: INBOUND_IN and OUTBOUND_IN.

It defines two TypeParameterMatcher to filter inboundMsg and outboundMsg:

    protected MessageToMessageCodec() {
        inboundMsgMatcher = TypeParameterMatcher.find(this, MessageToMessageCodec.class, "INBOUND_IN");
        outboundMsgMatcher = TypeParameterMatcher.find(this, MessageToMessageCodec.class, "OUTBOUND_IN");
    }
Copy the code

The channelRead and write methods are implemented, respectively, to read and write messages:

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

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        encoder.write(ctx, msg, promise);
    }
Copy the code

MessageToMessageDecoder and MessageToMessageEncoder

    private final MessageToMessageEncoder<Object> encoder = new MessageToMessageEncoder<Object>() {

        @Override
        public boolean acceptOutboundMessage(Object msg) throws Exception {
            return MessageToMessageCodec.this.acceptOutboundMessage(msg);
        }

        @Override
        @SuppressWarnings("unchecked")
        protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
            MessageToMessageCodec.this.encode(ctx, (OUTBOUND_IN) msg, out);
        }
    };

    private final MessageToMessageDecoder<Object> decoder = new MessageToMessageDecoder<Object>() {

        @Override
        public boolean acceptInboundMessage(Object msg) throws Exception {
            return MessageToMessageCodec.this.acceptInboundMessage(msg);
        }

        @Override
        @SuppressWarnings("unchecked")
        protected void decode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
            MessageToMessageCodec.this.decode(ctx, (INBOUND_IN) msg, out);
        }
    };
Copy the code

It can be seen that MessageToMessageCodec is actually the package of MessageToMessageDecoder and MessageToMessageEncoder. If you need to expand MessageToMessageCodec, The following two methods need to be implemented:

    protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, List<Object> out)
            throws Exception;

    protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, List<Object> out)
            throws Exception;
Copy the code

conclusion

The encoding framework for MessageToMessage provided in Netty is the basis for subsequent extensions to codecs. Only with a thorough understanding of the principles, we can use the new codec with ease.

This article is available at www.flydean.com/14-0-1-nett…

The most popular interpretation, the most profound dry goods, the most concise tutorial, many tips you didn’t know waiting for you to discover!

Welcome to pay attention to my public number: “procedures those things”, understand technology, more understand you!