前言
职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。类图如下:
职责链模式中主要包括以下两个角色:抽象处理者(handler)和具体处理者(concreteHandler),抽象处理者中声明处理方法,声明或直接定义传递方法,以将责任递交给下一个handler。
示例
AbstractHandler
/**
* @author jhz
* @date 18-10-19 下午11:02
*/
public abstract class AbstractHandler {
/**
* handler:存储下一个handler
*/
protected AbstractHandler nextHandler;
public abstract void doSomething();
public void setNextHandler(AbstractHandler handler){
this.nextHandler = handler;
}
public AbstractHandler getNextHandler(){
return nextHandler;
}
}
ConcreteHandler
/**
* @author jhz
* @date 18-10-19 下午11:06
*/
public class ConcreteHandler extends AbstractHandler {
@Override
public void doSomething() {
if (getNextHandler() != null){
System.out.println("交给下一个handler处理!");
getNextHandler().doSomething();
}
else{
System.out.println("do something!");
}
}
}
CLient
/**
* @author jhz
* @date 18-10-19 下午11:25
*/
public class CLient {
public static void main(String[] args) {
AbstractHandler firstHandler = new ConcreteHandler();
AbstractHandler secondHandler = new ConcreteHandler();
firstHandler.setNextHandler(secondHandler);
firstHandler.doSomething();
}
}
结果
职责链是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。接着我们来看看职责链在Netty中的应用。
在Netty中的应用
用过Netty的都知道,在边写服务端代码时,首先会定义ChannelPipline(抽象的数据管道),并通过initChannel对它进行初始化,在initChannel方法中会为这根“管道”添加一系列的channelHandler,这些handler都继承自ChannelInboundHandlerAdapter或者ChannelOutboundHandlerAdapter。前面已经提到了Netty是基于事件驱动机制,而这些事件在Netty中又被具体定义为两类:Inbound和Outbound:
借用官网的图来说明:
I/O Request
via Channel or
ChannelHandlerContext
|
+---------------------------------------------------+---------------+
| ChannelPipeline | |
| \|/ |
| +---------------------+ +-----------+----------+ |
| | Inbound Handler N | | Outbound Handler 1 | |
| +----------+----------+ +-----------+----------+ |
| /|\ | |
| | \|/ |
| +----------+----------+ +-----------+----------+ |
| | Inbound Handler N-1 | | Outbound Handler 2 | |
| +----------+----------+ +-----------+----------+ |
| /|\ . |
| . . |
| ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
| [ method call] [method call] |
| . . |
| . \|/ |
| +----------+----------+ +-----------+----------+ |
| | Inbound Handler 2 | | Outbound Handler M-1 | |
| +----------+----------+ +-----------+----------+ |
| /|\ | |
| | \|/ |
| +----------+----------+ +-----------+----------+ |
| | Inbound Handler 1 | | Outbound Handler M | |
| +----------+----------+ +-----------+----------+ |
| /|\ | |
+---------------+-----------------------------------+---------------+
| \|/
+---------------+-----------------------------------+---------------+
| | | |
| [ Socket.read() ] [ Socket.write() ] |
| |
| Netty Internal I/O Threads (Transport Implementation) |
+-------------------------------------------------------------------+
Inboud和Outbound事件,顾名思义,代表事件处理的方向,其中Outbound事件事件都是请求事件(request event), 即请求某件事情的发生, 然后通过 Outbound 事件进行通知;而Inbound事件则是通知事件,通常发生在 Channel 的状态的改变或 IO 事件就绪。这里可以看一下ChannelInboundHandlerAdapter的源码:
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel;
/**
* Abstract base class for {@link ChannelInboundHandler} implementations which provide
* implementations of all of their methods.
*
* <p>
* This implementation just forward the operation to the next {@link ChannelHandler} in the
* {@link ChannelPipeline}. Sub-classes may override a method implementation to change this.
* </p>
* <p>
* Be aware that messages are not released after the {@link #channelRead(ChannelHandlerContext, Object)}
* method returns automatically. If you are looking for a {@link ChannelInboundHandler} implementation that
* releases the received messages automatically, please see {@link SimpleChannelInboundHandler}.
* </p>
*/
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.
*/
@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.
*/
@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.
*/
@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.
*/
@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.
*/
@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.
*/
@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.
*/
@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.
*/
@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.
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
ctx.fireExceptionCaught(cause);
}
}
可以看到,在每个事件回调函数中,都有个firexxx方法,这其实就是职责链模式中的事件传输方法,父类中默认将其传递给下一个handler而不做处理,这就是职责沿着职责链传递的事件传输方法,以ChannelRegistered方法为例,看下在传递过程中做了什么事:
//调用下个Handler中的ChannelRegistered方法
@Override
public ChannelHandlerContext fireChannelRegistered() {
invokeChannelRegistered(findContextInbound());
return this;
}
//找到下一个Handler的ChannelHandlerContext,负责存储职责链(handler链表)以及类型(inbound or outbound)
private AbstractChannelHandlerContext findContextInbound() {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while (!ctx.inbound);
return ctx;
}
//执行下个Handler中的ChannelRegistered方法
static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
//获取下一个Handler所在事件执行器
EventExecutor executor = next.executor();
//如果该执行器仍在线程池(NIOEventLoop)中,则直接调用next所对应Handler的ChannelRegistered方法
if (executor.inEventLoop()) {
next.invokeChannelRegistered();
} else {
//否则,新起一个线程调用ChannelRegistered方法
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRegistered();
}
});
}
}
在Netty中,Inbound事件和Outbound事件包括以下几种:
Inbound 事件传播方法有:
ChannelHandlerContext.fireChannelRegistered()
ChannelHandlerContext.fireChannelActive()
ChannelHandlerContext.fireChannelRead(Object)
ChannelHandlerContext.fireChannelReadComplete()
ChannelHandlerContext.fireExceptionCaught(Throwable)
ChannelHandlerContext.fireUserEventTriggered(Object)
ChannelHandlerContext.fireChannelWritabilityChanged()
ChannelHandlerContext.fireChannelInactive()
ChannelHandlerContext.fireChannelUnregistered()
Oubound 事件传输方法有:
ChannelHandlerContext.bind(SocketAddress, ChannelPromise)
ChannelHandlerContext.connect(SocketAddress, SocketAddress, ChannelPromise)
ChannelHandlerContext.write(Object, ChannelPromise)
ChannelHandlerContext.flush()
ChannelHandlerContext.read()
ChannelHandlerContext.disconnect(ChannelPromise)
ChannelHandlerContext.close(ChannelPromise)
Outbound 事件的传播方向是 tail -> customContext -> head,而Inbound则是反过来,从head -> customContext -> tail。
小结
之前没搞清楚Inboud事件和Outbound事件的区别,今天通过阅读源码对handler的事件传递方向又了解了一些(以前以为都是head->tail)。