Netty源码之ChannelHandler

前一篇文章我们介绍了Netty中的ChannelPipeline,本文我们会继续基于源码,学习Netty中的重要接口ChannelHandler


前言

我们知道Netty中将I/O事件分为了两类:入站(Inbound)和出站(Outbound

入站事件又细分为了:
通道注册完成/取消注册,通道激活/通道失活通道读/读完成异常处理

出站事件分为:

绑定端口连接/取消连接端口,关闭事件以及冲刷事件

源码分析

该接口中定义了三个方法(有一个已废弃),一个注解

// 1. 
void handlerAdded(ChannelHandlerContext ctx) throws Exception;

// 2. 
void handlerRemoved(ChannelHandlerContext ctx) throws Exception;

@Deprecated
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;

@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Sharable {
    // no value
}

方法中的1. 2. 两个方法是回调钩子函数

  1. 用于当该handler被添加到实际的handlerContext中并已经开始处理IO事件时被调用,即pipeline的add方法中会触发该函数调用,可以用于当handler被添加到pipeline中打印log日志

  2. 当该handler从handlerContext被移除且不再处理事件时被调用,即pipeline的remove方法中会触发该函数调用,可以用于当handler被从pipeline中移除时打印log日志

另外的内部注解Sharable,是一种标识,表示的是该handler是否是共享的,如果添加了该注解,即表示该handler只会存在一个实例,即在所有pipeline中共享,我们可以利用这个特性做类似在线人数统计的功能

ChannelHandler接口的定义比较简单,下面主要看其两个重要的子接口


该接口继承了ChannelHandler,表示的是入站处理器,那既然是入站处理器,就应该定义入站事件相关处理方法,源码不多,这里直接copy过来

public interface ChannelInboundHandler extends ChannelHandler {

/**
 * The {@link Channel} of the {@link ChannelHandlerContext} was registered with its {@link EventLoop}
 */
void channelRegistered(ChannelHandlerContext ctx) throws Exception;

/**
 * The {@link Channel} of the {@link ChannelHandlerContext} was unregistered from its {@link EventLoop}
 */
void channelUnregistered(ChannelHandlerContext ctx) throws Exception;

/**
 * The {@link Channel} of the {@link ChannelHandlerContext} is now active
 */
void channelActive(ChannelHandlerContext ctx) throws Exception;

/**
 * The {@link Channel} of the {@link ChannelHandlerContext} was registered is now inactive and reached its
 * end of lifetime.
 */
void channelInactive(ChannelHandlerContext ctx) throws Exception;

/**
 * Invoked when the current {@link Channel} has read a message from the peer.
 */
void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;

/**
 * Invoked when the last message read by the current read operation has been consumed by
 * {@link #channelRead(ChannelHandlerContext, Object)}.  If {@link ChannelOption#AUTO_READ} is off, no further
 * attempt to read an inbound data from the current {@link Channel} will be made until
 * {@link ChannelHandlerContext#read()} is called.
 */
void channelReadComplete(ChannelHandlerContext ctx) throws Exception;

/**
 * Gets called if an user event was triggered.
 */
void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;

/**
 * Gets called once the writable state of a {@link Channel} changed. You can check the state with
 * {@link Channel#isWritable()}.
 */
void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;

/**
 * Gets called if a {@link Throwable} was thrown.
 */
@Override
@SuppressWarnings("deprecation")
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}

可以看到这里主要定义了channelRegisteredchannelUnregisteredchannelActivechannelInactivechannelReadchannelReadComplete以及异常处理exceptionCaught等方法

看了入站处理器接口,接下来我们看看出站处理器接口

代码不多,我们同样copy源码过来

public interface ChannelOutboundHandler extends ChannelHandler {
/**
 * Called once a bind operation is made.
 *
 * @param ctx           the {@link ChannelHandlerContext} for which the bind operation is made
 * @param localAddress  the {@link SocketAddress} to which it should bound
 * @param promise       the {@link ChannelPromise} to notify once the operation completes
 * @throws Exception    thrown if an error occurs
 */
void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;

/**
 * Called once a connect operation is made.
 *
 * @param ctx               the {@link ChannelHandlerContext} for which the connect operation is made
 * @param remoteAddress     the {@link SocketAddress} to which it should connect
 * @param localAddress      the {@link SocketAddress} which is used as source on connect
 * @param promise           the {@link ChannelPromise} to notify once the operation completes
 * @throws Exception        thrown if an error occurs
 */
void connect(
        ChannelHandlerContext ctx, SocketAddress remoteAddress,
        SocketAddress localAddress, ChannelPromise promise) throws Exception;

/**
 * Called once a disconnect operation is made.
 *
 * @param ctx               the {@link ChannelHandlerContext} for which the disconnect operation is made
 * @param promise           the {@link ChannelPromise} to notify once the operation completes
 * @throws Exception        thrown if an error occurs
 */
void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

/**
 * Called once a close operation is made.
 *
 * @param ctx               the {@link ChannelHandlerContext} for which the close operation is made
 * @param promise           the {@link ChannelPromise} to notify once the operation completes
 * @throws Exception        thrown if an error occurs
 */
void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

/**
 * Called once a deregister operation is made from the current registered {@link EventLoop}.
 *
 * @param ctx               the {@link ChannelHandlerContext} for which the close operation is made
 * @param promise           the {@link ChannelPromise} to notify once the operation completes
 * @throws Exception        thrown if an error occurs
 */
void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

/**
 * Intercepts {@link ChannelHandlerContext#read()}.
 */
void read(ChannelHandlerContext ctx) throws Exception;

/**
* Called once a write operation is made. The write operation will write the messages through the
 * {@link ChannelPipeline}. Those are then ready to be flushed to the actual {@link Channel} once
 * {@link Channel#flush()} is called
 *
 * @param ctx               the {@link ChannelHandlerContext} for which the write operation is made
 * @param msg               the message to write
 * @param promise           the {@link ChannelPromise} to notify once the operation completes
 * @throws Exception        thrown if an error occurs
 */
void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;

/**
 * Called once a flush operation is made. The flush operation will try to flush out all previous written messages
 * that are pending.
 *
 * @param ctx               the {@link ChannelHandlerContext} for which the flush operation is made
 * @throws Exception        thrown if an error occurs
 */
void flush(ChannelHandlerContext ctx) throws Exception;
}

主要关注出站事件处理write以及flush即可

出站和入站处理器接口源码就是这么多东西,此外Netty还针对二者分别提供了相应的适配器类,我们主要看看它们

入站事件处理适配器

  • UML


    ChannelInboundHandlerAdapter.png
  • ChannelHandlerAdapter

该类是一个抽象类,实现了ChannelHandler接口,做了简单的方法实现

public abstract class ChannelHandlerAdapter implements ChannelHandler {

// Not using volatile because it's used only for a sanity check.
boolean added;

/**
 * Throws {@link IllegalStateException} if {@link ChannelHandlerAdapter#isSharable()} returns {@code true}
 */
protected void ensureNotSharable() {
    if (isSharable()) {
        throw new IllegalStateException("ChannelHandler " + getClass().getName() + " is not allowed to be shared");
    }
}

/**
 * Return {@code true} if the implementation is {@link Sharable} and so can be added
 * to different {@link ChannelPipeline}s.
 */
public boolean isSharable() {
    /**
     * Cache the result of {@link Sharable} annotation detection to workaround a condition. We use a
     * {@link ThreadLocal} and {@link WeakHashMap} to eliminate the volatile write/reads. Using different
     * {@link WeakHashMap} instances per {@link Thread} is good enough for us and the number of
     * {@link Thread}s are quite limited anyway.
     *
     * See #2289.
     */
    Class clazz = getClass();
    Map, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();
    Boolean sharable = cache.get(clazz);
    if (sharable == null) {
        sharable = clazz.isAnnotationPresent(Sharable.class);
        cache.put(clazz, sharable);
    }
    return sharable;
}

/**
 * Do nothing by default, sub-classes may override this method.
 */
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    // NOOP
}

/**
 * Do nothing by default, sub-classes may override this method.
 */
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
    // NOOP
}
}

注意该抽象类中定义了isSharable函数,用于判断handler是否是可共享的(即加没加注解Shareable

而ChannelInboundHandlerAdapter类继承了ChannelHandlerAdapter且实现了ChannelInboundHandler接口,下面看看其如何实现的

public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler {

/**
 * Calls {@link ChannelHandlerContext#fireChannelRegistered()} to forward
 * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.
 *
 * Sub-classes may override this method to change behavior.
 */
@Skip
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
    ctx.fireChannelRegistered();
}

/**
 * Calls {@link ChannelHandlerContext#fireChannelUnregistered()} to forward
 * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.
 *
 * Sub-classes may override this method to change behavior.
 */
@Skip
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
    ctx.fireChannelUnregistered();
}

/**
 * Calls {@link ChannelHandlerContext#fireChannelActive()} to forward
 * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.
 *
 * Sub-classes may override this method to change behavior.
 */
@Skip
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    ctx.fireChannelActive();
}

/**
 * Calls {@link ChannelHandlerContext#fireChannelInactive()} to forward
 * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.
 *
 * Sub-classes may override this method to change behavior.
 */
@Skip
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    ctx.fireChannelInactive();
}

/**
 * Calls {@link ChannelHandlerContext#fireChannelRead(Object)} to forward
 * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.
 *
 * Sub-classes may override this method to change behavior.
 */
@Skip
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    ctx.fireChannelRead(msg);
}

/**
 * Calls {@link ChannelHandlerContext#fireChannelReadComplete()} to forward
 * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.
 *
 * Sub-classes may override this method to change behavior.
 */
@Skip
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    ctx.fireChannelReadComplete();
}

/**
 * Calls {@link ChannelHandlerContext#fireUserEventTriggered(Object)} to forward
 * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.
 *
 * Sub-classes may override this method to change behavior.
 */
@Skip
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    ctx.fireUserEventTriggered(evt);
}

/**
 * Calls {@link ChannelHandlerContext#fireChannelWritabilityChanged()} to forward
 * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}.
 *
 * Sub-classes may override this method to change behavior.
 */
@Skip
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
    ctx.fireChannelWritabilityChanged();
}

/**
 * Calls {@link ChannelHandlerContext#fireExceptionCaught(Throwable)} to forward
 * to the next {@link ChannelHandler} in the {@link ChannelPipeline}.
 *
 * Sub-classes may override this method to change behavior.
 */
@Skip
@Override
@SuppressWarnings("deprecation")
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
        throws Exception {
    ctx.fireExceptionCaught(cause);
}

}

可见该Adapter类中的所有方法只是简单的调用了ctx的激活方法,即代表其只是简单的将事件传递下去而已


同理,出站处理器也是简单的调用ctx的激活方法,将出站事件传递下去而已,这里简单贴出两个重要的方法

public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelOutboundHandler {

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

/**
 * Calls {@link ChannelHandlerContext#flush()} to forward
 * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}.
 *
 * Sub-classes may override this method to change behavior.
 */
@Skip
@Override
public void flush(ChannelHandlerContext ctx) throws Exception {
    ctx.flush();
}
}

总结

  1. 根据I/O事件的类型,Netty事件处理器分为了入站出站两类

  2. 实际开发中可以直接继承Netty提供的Adapter类,只需要重写需要的IO事件处理方法即可,其余方法Adapter已经实现了继续传递事件

  3. ChannelHandler接口内部定义了一个注解(@Shareable),该注解标识的Handler会在pipeline间共享,即只会存在一个该Handler,可以通过该Handler实现类似统计在线人数的功能

你可能感兴趣的:(Netty源码之ChannelHandler)