Nettyソース分析(十二)チャネルとチャネルハンドラー及びチャネルハンドラーContextの関係分析

18383 ワード

まだ前のinitコードのエントリで、前節ではChannelOptionとAttributeKeyを紹介しましたが、今回はChannelとChannelHandlerおよびChannelHandlerContextの関係分析についてお話しします.
void init(Channel channel) throws Exception {
        final Map, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }

        final Map, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey key = (AttributeKey) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }

        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry, Object>[] currentChildOptions;
        final Entry, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
        }

        p.addLast(new ChannelInitializer() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

まずChannelInitializerからdocを見てみましょう

/**
 * A special {@link ChannelInboundHandler} which offers an easy way to initialize a {@link Channel} once it was
 * registered to its {@link EventLoop}.
 *      ChannelInboundHandler,            Channel,     EventLoop      。
 * Implementations are most often used in the context of {@link Bootstrap#handler(ChannelHandler)} ,
 * {@link ServerBootstrap#handler(ChannelHandler)} and {@link ServerBootstrap#childHandler(ChannelHandler)} to
 * setup the {@link ChannelPipeline} of a {@link Channel}.
 *          Bootstrap#handler(ChannelHandler)、ServerBootstrap#handler(ChannelHandler)、
 * ServerBootstrap#childHandler(ChannelHandler)     Channel ChannelPipeline
 * 

 *      :
 * public class MyChannelInitializer extends {@link ChannelInitializer} {
 *     public void initChannel({@link Channel} channel) {
 *         channel.pipeline().addLast("myHandler", new MyHandler());
 *     }
 * }
 *
 * {@link ServerBootstrap} bootstrap = ...;
 * ...
 * bootstrap.childHandler(new MyChannelInitializer());
 * ...
 * 
* Be aware that this class is marked as {@link Sharable} and so the implementation must be safe to be re-used.
*このクラスはSharableと されているので、 にはスレッドが である があります.
* @param A sub-type of {@link Channel}
*/
@Sharable
public abstract class ChannelInitializer extends ChannelInboundHandlerAdapter {
}
プログラム び しp.addLast(new ChannelInitializer()…というようにChannelInitializerをChannelPipelineに れるのですが、ChannelPipeline.addLast()の はどうなっているのでしょうか.
    /**
     * Inserts {@link ChannelHandler}s at the last position of this pipeline.
     *  pipeline           ChannelHandler
     * @param handlers  the handlers to insert last
     */
    ChannelPipeline addLast(ChannelHandler... handlers);

ChannelPipelineの クラスを するには、 の に います.
public class DefaultChannelPipeline implements ChannelPipeline {
... 
    @Override
    public final ChannelPipeline addLast(ChannelHandler... handlers) {
        return addLast(null, handlers);
    }
    ... 

addLast(null,handlers)が び され、 のパラメータはnullです.
 executor null
    public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) {
        if (handlers == null) {
            throw new NullPointerException("handlers");
        }
//    ,  addLast(executor, null, h)  ,       null
        for (ChannelHandler h: handlers) {
            if (h == null) {
                break;
            }
            addLast(executor, null, h);
        }

        return this;
    }

な に ります.groupはnull、nameはnullです.
    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;//      AbstractChannelHandlerContext 
        synchronized (this) {
            checkMultiplicity(handler);
            newCtx = newContext(group, filterName(name, handler), handler);
            addLast0(newCtx);

            // If the registered is false it means that the channel was not registered on an eventloop yet.
            // In this case we add the context to the pipeline and add a task that will call
            // ChannelHandler.handlerAdded(...) once the channel is registered.
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                newCtx.setAddPending();
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }
        callHandlerAdded0(newCtx);
        return this;
    }

AbstractChannelHandlerContextについては、AbstractChannelHandlerContextのdocがどのようなものかを てみましょう.
abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
        implements ChannelHandlerContext, ResourceLeakHint {

のインターフェイスに りますChannelHandlerContext:

/**
 * Enables a {@link ChannelHandler} to interact with its {@link ChannelPipeline}
 * and other handlers. Among other things a handler can notify the next {@link ChannelHandler} in the
 * {@link ChannelPipeline} as well as modify the {@link ChannelPipeline} it belongs to dynamically.
 *  ChannelHandler   ChannelPipeline              ,    ChannelPipeline     ChannelHandler,
 *           ChannelPipeline
 * 

Notify

* * You can notify the closest handler in the same {@link ChannelPipeline} by calling one of the various methods * provided here. * ChannelPipeline handler * Please refer to {@link ChannelPipeline} to understand how an event flows. * ChannelPipeline 。 *

Modifying a pipeline

* pipeline * You can get the {@link ChannelPipeline} your handler belongs to by calling * {@link #pipeline()}. A non-trivial application could insert, remove, or * replace handlers in the pipeline dynamically at runtime. * pipeline() ChannelPipeline, pipeline , 。 *

Retrieving for later use

* * You can keep the {@link ChannelHandlerContext} for later use, such as * triggering an event outside the handler methods, even from a different thread. * ChannelHandlerContext , handler , 。 *

 * public class MyHandler extends {@link ChannelDuplexHandler} {
 *
 *     private {@link ChannelHandlerContext} ctx;
 *
 *     public void beforeAdd({@link ChannelHandlerContext} ctx) {
 *         this.ctx = ctx;</b>//    ChannelHandlerContext
 *     }
 *
 *     public void login(String username, password) {
 *         ctx.write(new LoginMessage(username, password));//           
 *     }
 *     ...
 * }
 * 
*
* Storing stateful information
*ステータス の
* {@link #attr(AttributeKey)} allow you to
* store and access stateful information that is related with a handler and its
* context. Please refer to {@link ChannelHandler} to learn various recommended
* ways to manage stateful information.
*AttributeKeyでは、 するhandlerとそのコンテキストのステータス を できます.ChannelHandlerを して、ステータス を する を できます.
* A handler can have more than one context
*1つのhandlerには のコンテキストがあります.
* Please note that a {@link ChannelHandler} instance can be added to more than
* one {@link ChannelPipeline}. It means a single {@link ChannelHandler}
* instance can have more than one {@link ChannelHandlerContext} and therefore
* the single instance can be invoked with different
* {@link ChannelHandlerContext}s if it is added to one or more
* {@link ChannelPipeline}s more than once.
*1つのChannelHandlerは、1つのChannelPipelineに することができ、1つの のChannelHandlerインスタンスが あることを する
*ChannelHandlerContextは、 のChannelHandlerContextによって のインスタンスを び すことができ、ChannelHandlerインスタンスが された に び すことができる.
*
* For example, the following handler will have as many independent {@link AttributeKey}s
* as how many times it is added to pipelines, regardless if it is added to the
* same pipeline multiple times or added to different pipelines multiple times:
*

 * public class FactorialHandler extends {@link ChannelInboundHandlerAdapter} {
 *
 *   private final {@link AttributeKey}<{@link Integer}> counter = {@link AttributeKey}.valueOf("counter");
 *
 *   // This handler will receive a sequence of increasing integers starting
 *   // from 1.
 *   {@code @Override}
 *   public void channelRead({@link ChannelHandlerContext} ctx, Object msg) {
 *     Integer a = ctx.attr(counter).get();
 *
 *     if (a == null) {
 *       a = 1;
 *     }
 *
 *     attr.set(a * (Integer) msg);
 *   }
 * }
 *
 * // Different context objects are given to "f1", "f2", "f3", and "f4" even if
 * // they refer to the same handler instance.  Because the FactorialHandler
 * // stores its state in a context object (using an {@link AttributeKey}), the factorial is
 * // calculated correctly 4 times once the two pipelines (p1 and p2) are active.
 *   "f1", "f2", "f3", and "f4"        ,           ,  FactorialHandler                
 * (  AttributeKey),        factorial ,factorial        2 pipelines (p1   p2) 。
 * FactorialHandler fh = new FactorialHandler();
 *
 * {@link ChannelPipeline} p1 = {@link Channels}.pipeline();
 * p1.addLast("f1", fh);
 * p1.addLast("f2", fh);
 *
 * {@link ChannelPipeline} p2 = {@link Channels}.pipeline();
 * p2.addLast("f3", fh);
 * p2.addLast("f4", fh);
 * 
*
* Additional resources worth reading
*
* Please refer to the {@link ChannelHandler}, and
* {@link ChannelPipeline} to find out more about inbound and outbound operations,
* what fundamental differences they have, how they flow in a pipeline, and how to handle
* the operation in your application.
*ChannelHandlerとChannelPipelineを して、アウトスタックとインスタックの 、 らの の も な い、pipelineでどのように れているのか、アプリケーションでどのように されているのかを してください.
*/
public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker {