netty default Channel pipeline - channel processor removal and replacement

netty Inboudn/Outbound Channel Invoker: http://donald-draper.iteye.com/blog/2388233
netty Asynchronous Task-ChannelFuture: http://donald-draper.iteye.com/blog/2388297
netty Pipeline Definition-ChannelPipeline: http://donald-draper.iteye.com/blog/2388453
netty default Channel pipeline initialization: http://donald-draper.iteye.com/blog/2388613
netty default Channel pipeline - adding channel handlers: http: //donald-draper.iteye.com/blog/2388726Introduction
:
In the last article, we saw the process of adding channel processors to the Channel pipeline. Let's review it first:
Add the channel handler to the pipe header, and check for the first time whether the channel handler is in shared mode. If it is not shared and has been added, an exception will be thrown; check if the channel handler name is in the pipeline and whether there is a corresponding channel handler context. There is an exception thrown; according to the event executor, processor name, and processor, construct the processor context; add the processor upper context to the pipeline context chain header; if the channel is not registered to the event loop, when the context is added to the pipeline, create an add The channel handler calls back the task and adds the task to the callback task chain of the pipeline. When the channel is registered to the event loop, the handlerAdded event of the channel handler is triggered. If it is registered, a thread is created to call the handlerAdded event method of the channel handler. , and update the context state to added, and hand it over to the event executor for execution; it is best to call the callHandlerAdded0 method to ensure that the handlerAdded event method of the channel handler is called to update the context state to added. The other operations of last (adding to the end of the pipeline), before (adding the front of the specified context), and after (adding the back of the specified context) are basically the same as the addfirst idea, the difference is the position of adding to the pipeline context chain.
Today, let's take a look at removing a channel processor:
remove a channel processor of a specified type
@Override
public final ChannelPipeline remove(ChannelHandler handler) {
    //First obtain the context corresponding to the channel processor, and then delegate to the context removal method
    remove(getContextOrDie(handler));
    return this;
}
//Get the context corresponding to the processor
private AbstractChannelHandlerContext getContextOrDie(ChannelHandler handler) {
    //Actual get context method
    AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handler);
    if (ctx == null) {
        throw new NoSuchElementException(handler.getClass().getName());
    } else {
        return ctx;
    }
}

@Override
public final ChannelHandlerContext context(ChannelHandler handler) {
    if (handler == null) {
        throw new NullPointerException("handler");
    }
    AbstractChannelHandlerContext ctx = head.next;
    for (;;) {
        if (ctx == null) {
            return null;
        }
	//The context handler handle is the same as the handler, then return
        if (ctx.handler() == handler) {
            return ctx;
        }
        ctx = ctx.next;
    }
}



//Remove channel processing by name
@Override
public final ChannelHandler remove(String name) {
    return remove(getContextOrDie(name)).handler();
}

 private AbstractChannelHandlerContext getContextOrDie(String name) {
        AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(name);
        if (ctx == null) {
            throw new NoSuchElementException(name);
        } else {
            return ctx;
        }
}

@Override
   public final ChannelHandlerContext context(String name) {
       if (name == null) {
           throw new NullPointerException("name");
       }
       return context0(name);
   }

private AbstractChannelHandlerContext context0(String name) {
    AbstractChannelHandlerContext context = head.next;
    while (context != tail) {
        // Return context with the same context name as the specified handler name
        if (context.name().equals(name)) {
            return context;
        }
        context = context.next;
    }
    return null;
}


@SuppressWarnings("unchecked")
//Remove the specified parameter type handler
@Override
public final <T extends ChannelHandler> T remove(Class<T> handlerType) {
    return (T) remove(getContextOrDie(handlerType)).handler();
}


private AbstractChannelHandlerContext getContextOrDie(Class<? extends ChannelHandler> handlerType) {
     AbstractChannelHandlerContext ctx = (AbstractChannelHandlerContext) context(handlerType);
     if (ctx == null) {
         throw new NoSuchElementException(handlerType.getName());
     } else {
         return ctx;
     }
 }

@Override
public final ChannelHandlerContext context(Class<? extends ChannelHandler> handlerType) {
    if (handlerType == null) {
        throw new NullPointerException("handlerType");
    }

    AbstractChannelHandlerContext ctx = head.next;
    for (;;) {
        if (ctx == null) {
            return null;
        }
	//If the handler type associated with the context is handlerType, return the context
        if (handlerType.isAssignableFrom(ctx.handler().getClass())) {
            return ctx;
        }
        ctx = ctx.next;
    }
}

The above method has nothing to say, it is very simple.
It can be seen from the above that whether it is based on the name, the processor handle, or the type, the corresponding
processor .
private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) {
    //Assert the removed context is neither head nor tail
    assert ctx != head && ctx != tail;
    synchronized (this) {
        // remove context
        remove0(ctx);
        // If the registered is false it means that the channel was not registered on an eventloop yet.
        // In this case we remove the context from the pipeline and add a task that will call
        // ChannelHandler.handlerRemoved(...) once the channel is registered.
        if (!registered) {
	    //If the channel has been deregistered from the event loop, add a remove callback task
            callHandlerCallbackLater(ctx, false);
            return ctx;
        }
	//Get the channel handler context event executor
        EventExecutor executor = ctx.executor();
        if (!executor.inEventLoop()) {
	   //If the event executor is not in the current transaction loop, create a thread to execute the channel processor to remove related events and update the context state,
	   // Thread delegates to context event executor
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    callHandlerRemoved0(ctx);
                }
            });
            return ctx;
        }
    }
    //
    callHandlerRemoved0(ctx);
    return ctx;
}

Remove the channel context method, there are a few points to pay attention to
1.
// remove context
remove0(ctx);

private static void remove0(AbstractChannelHandlerContext ctx) {
     AbstractChannelHandlerContext prev = ctx.prev;
     AbstractChannelHandlerContext next = ctx.next;
     prev.next = next;
     next.prev = prev;
 }

2.
if (!registered) {
    //If the channel has been deregistered from the event loop, add a remove callback task
    callHandlerCallbackLater(ctx, false);
    return ctx;
}

//Add the remove context callback task to the pipeline callback task chain
private void callHandlerCallbackLater(AbstractChannelHandlerContext ctx, boolean added) {
      assert !registered;
      PendingHandlerCallback task = added ? new PendingHandlerAddedTask(ctx) : new PendingHandlerRemovedTask(ctx);
      PendingHandlerCallback pending = pendingHandlerCallbackHead;
      if (pending == null) {
          pendingHandlerCallbackHead = task;
      } else {
          // Find the tail of the linked-list.
          while (pending.next != null) {
              pending = pending.next;
          }
          pending.next = task;
      }
  }

private final class PendingHandlerRemovedTask extends PendingHandlerCallback {

        PendingHandlerRemovedTask(AbstractChannelHandlerContext ctx) {
            super(ctx);
        }

        @Override
        public void run() {
	    / / Trigger the removal event, and update the upper and lower status to the removed status
            callHandlerRemoved0(ctx);
        }
      
        @Override
        void execute() {
            EventExecutor executor = ctx.executor();
            if (executor.inEventLoop()) {
                callHandlerRemoved0(ctx);
            } else {
	        //If the removal thread is not in the current transaction loop, the removal task is handed over to the context-associated event executor
                try {
                    executor.execute(this);
                } catch (RejectedExecutionException e) {
                    if (logger.isWarnEnabled()) {
                        logger.warn(
                                "Can't invoke handlerRemoved() as the EventExecutor {} rejected it," +
                                        " removing handler {}.", executor, ctx.name(), e);
                    }
                    // remove0(...) was call before so just call AbstractChannelHandlerContext.setRemoved().
                    ctx.setRemoved();
                }
            }
        }
    }

3.
/ / Trigger the processing removal event, and update the upper and lower status to removed
private void callHandlerRemoved0(final AbstractChannelHandlerContext ctx) {
      // Notify the complete removal.
      try {
          try {
	        //Trigger the handlerRemoved event associated with the context
              ctx.handler().handlerRemoved(ctx);
          } finally {
	        //Update the context state to removed
              ctx.setRemoved();
          }
      } catch (Throwable t) {
          fireExceptionCaught(new ChannelPipelineException(
                  ctx.handler().getClass().getName() + ".handlerRemoved() has thrown an exception.", t));
      }
}

As can be seen from the above, the actual removal of the channel processor context is to first remove the corresponding context from the pipeline. If the channel has been deregistered from the event loop,
add the removal callback task to the pipeline callback task chain, otherwise create a thread directly (Trigger the handlerRemoved event associated with the context,
update the context status to removed), and execute the event executor associated with the context.
With the above foreshadowing, the following two methods should be well understood
//Remove the handler at the head of the pipe
@Override
  public final ChannelHandler removeFirst() {
      if (head.next == tail) {
          throw new NoSuchElementException();
      }
      return remove(head.next).handler();
  }
remove the handler at the end of the pipeline
  @Override
  public final ChannelHandler removeLast() {
      if (head.next == tail) {
          throw new NoSuchElementException();
      }
      return remove(tail.prev).handler();
}

The head and tail in the above are not puppet nodes, but the first and last elements of the actual context.
Let's look at the replacement channel processor operation again:
@Override
public final ChannelPipeline replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler) {
    replace(getContextOrDie(oldHandler), newName, newHandler);
    return this;
}
@Override
public final ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler) {
    return replace(getContextOrDie(oldName), newName, newHandler);
}
@Override
@SuppressWarnings("unchecked")
public final <T extends ChannelHandler> T replace(
        Class<T> oldHandlerType, String newName, ChannelHandler newHandler) {
    return (T) replace(getContextOrDie(oldHandlerType), newName, newHandler);
}

It can be seen from the above that whether the channel handler is replaced by name, handler handle, or type, the corresponding handler context is first obtained, and then delegated to the replace(final AbstractChannelHandlerContext ctx, String newName, ChannelHandler newHandler)
method.
private ChannelHandler replace(
        final AbstractChannelHandlerContext ctx, String newName, ChannelHandler newHandler) {
    assert ctx != head && ctx != tail;
    final AbstractChannelHandlerContext newCtx;
    synchronized (this) {
        checkMultiplicity(newHandler);
        if (newName == null) {
            newName = generateName(newHandler);
        } else {
            boolean sameName = ctx.name().equals(newName);
            if (!sameName) {
                checkDuplicateName(newName);
            }
        }
	//Create a context for the new handler
        newCtx = newContext(ctx.executor, newName, newHandler);
	// Replace the original old context in the pipeline context chain with the new processing context
        replace0(ctx, newCtx);

        // If the registered is false it means that the channel was not registered on an eventloop yet.
        // In this case we replace the context in the pipeline
        // and add a task that will call ChannelHandler.handlerAdded(...) and
        // ChannelHandler.handlerRemoved(...) once the channel is registered.
        if (!registered) {
	    //Add a callback task for adding a new context
            callHandlerCallbackLater(newCtx, true);
	    //Add the removal callback task of the old context
            callHandlerCallbackLater(ctx, false);
            return ctx.handler();
        }
        EventExecutor executor = ctx.executor();
        if (!executor.inEventLoop()) {
            executor.execute(new Runnable() {
	       //if
                @Override
                public void run() {
		    //Add first, then remove, because the removal operation will trigger channelRead and flush events, and these event processing must be after the handlerAdded event
                    // Invoke newHandler.handlerAdded() first (i.e. before oldHandler.handlerRemoved() is invoked)
                    // because callHandlerRemoved() will trigger channelRead() or flush() on newHandler and
                    // those event handlers must be called after handlerAdded().
		    //Update the handlerAdded of the processor corresponding to the new context, and update, the new upper and lower status is added
                    callHandlerAdded0(newCtx);
		    / / Update the handlerRemoved of the processor corresponding to the old context, and update, the new and old up and down status is added
                    callHandlerRemoved0(ctx);
                }
            });
            return ctx.handler();
        }
    }
    // Invoke newHandler.handlerAdded() first (i.e. before oldHandler.handlerRemoved() is invoked)
    // because callHandlerRemoved() will trigger channelRead() or flush() on newHandler and those
    // event handlers must be called after handlerAdded().
    callHandlerAdded0(newCtx);
    callHandlerRemoved0(ctx);
    return ctx.handler();
}
//replace context
private static void replace0(AbstractChannelHandlerContext oldCtx, AbstractChannelHandlerContext newCtx) {
    AbstractChannelHandlerContext prev = oldCtx.prev;
    AbstractChannelHandlerContext next = oldCtx.next;
    newCtx.prev = prev;
    newCtx.next = next;

    // Finish the replacement of oldCtx with newCtx in the linked list.
    // Note that this doesn't mean events will be sent to the new handler immediately
    // because we are currently at the event handler thread and no more than one handler methods can be invoked
    // at the same time (we ensured that in replace().)
    prev.next = newCtx;
    next.prev = newCtx;

    // update the reference to the replacement so forward of buffered content will work correctly
    // The predecessor and successor of the main original context point to the new context at the same time, so that the remaining buf content can be forwarded
    oldCtx.prev = newCtx;
    oldCtx.next = newCtx;
}

As can be seen from the above, in fact, to replace the context is to add a new context to the position of
the original context in the pipeline, and point the predecessor and successor of the original context to the new context at the same time, so as to forward the remaining buf content.
It can be simply understood as adding a new context and removing the original context. Note that it must be added first and then removed,
because the removal operation will trigger channelRead and flush events, and these events must be processed after the

handlerAdded       . The handler handle, or the channel handler is removed according to the type, it is to first obtain the corresponding handler context, remove the corresponding context from the pipeline, and add the removal callback task to the pipeline callback task if the channel has been deregistered from the event loop. Chain, otherwise directly create a thread (trigger the context-associated handler handlerRemoved event, update the context status to removed), and execute the context-associated event executor.       Whether it is to replace the channel processor by name, processor handle, or by type, first obtain the corresponding processor context, then add a new context to the position of the original context in the pipeline, and point the predecessor and successor of the original context to the new context at the same time Context in order to forward the remaining buf content; it can be simply understood as adding a new context and removing the original context. Note that it must be added first and then removed, because the removal operation will trigger channelRead and flush events, and these events must be handled in the handlerAdded event. after .




Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326308259&siteId=291194637