Netty基本介绍,参考juejin.cn/post/740884…

1、Netty的责任链模式

1.1 责任链模式实现样例

基于上图,写一个责任链模式的案例如下:

从下面的例子我们可以知道,责任链模式包含下面几个重要的部分:

  • HandlerChainContext:hander上下文,也就是责任链中的节点,持有一个handler,并有指向下一个节点的指针
  • Handler: 责任处理器
  • Pipeline:维护整个责任链,添加删除责任处理器
public class Pipeline {
    //handler上下文,维护链表和负责链表的执行
    class HandlerChainContext {
        HandlerChainContext next;// 持有下一个节点:单链表
        AbstractHandler handler;
        public HandlerChainContext(AbstractHandler handler) {
            this.handler = handler;
        }
        // 将节点持有下去
        void handler(Object arg0) {
            this.handler.doHandler(this, arg0);
        }
        // 继续执行下一个
        void runNext(Object arg0) {
            if (this.next != null) {
                this.next.handler(arg0);
            }
        }
    }
    // 持有上下文(可以获得需要的数据,属性)
    public HandlerChainContext context = new HandlerChainContext(new AbstractHandler() {
        @Override
        void doHandler(HandlerChainContext context, Object arg0) {
            System.out.println("折扣前"+arg0);
            context.runNext(arg0);
        }
    });

    // 添加责任链
    public void addLast(AbstractHandler handler) {
        HandlerChainContext next = context;
        while (next.next != null) {
            next = next.next;
        }
        next.next = new HandlerChainContext(handler);
    }

    // 开始调用
    public void requestProcess(Object arg0) {
        context.handler(arg0);
    }
    //处理器抽象类
    static abstract class AbstractHandler {
        abstract void doHandler(HandlerChainContext context, Object arg0);
    }
    //具体的处理器实现类(购物折扣1)
    static class Handler1 extends AbstractHandler {
        @Override
        void doHandler(HandlerChainContext handlerChainContext, Object arg0) {
            System.out.println("--首次购买打9折!");
            arg0 = new DecimalFormat("0.00").format(Double.valueOf(arg0.toString())*0.9);
            System.out.println("折扣后金额:"+arg0);
            // 继续执行下一个
            handlerChainContext.runNext(arg0);
        }
    }
    //具体的处理器实现类(购物折扣2)
    static class Handler2 extends AbstractHandler {
        @Override
        void doHandler(HandlerChainContext handlerChainContext, Object arg0) {
            System.out.println("--满200减20!");
            if(Double.valueOf(arg0.toString()) >= 200){
                arg0 = Double.valueOf(arg0.toString())-20;
                // 继续执行下一个
                System.out.println("折扣后金额:"+arg0);
                handlerChainContext.runNext(arg0);
            }else{
                System.out.println("不满足条件,折扣结束:"+arg0);
            }
        }
    }
    //具体的处理器实现类(购物折扣3)
    static class Handler3 extends AbstractHandler {
        @Override
        void doHandler(HandlerChainContext handlerChainContext, Object arg0) {
            System.out.println("--第二件减10元!");
            arg0 = Double.valueOf(arg0.toString())-10;
            System.out.println("折扣后金额:"+arg0);
            // 继续执行下一个
            handlerChainContext.runNext(arg0);
        }
    }

    public static void main(String[] args) {
        Pipeline p = new Pipeline();
        p.addLast(new Handler1());
        p.addLast(new Handler2());
        p.addLast(new Handler3());
        p.requestProcess("150");
    }
}

1.2 Netty的责任处理接口

如下图:责任处理接口分为

1.2.1 ChannelInboundHandler

入站处理器方法(注册、渠道读、渠道激活等等)

1.2.2 ChannelOutboundHandler

出站处理器方法(绑定、连接、读、写、刷新等等)

1.3 责任添加删除接口ChannelPipeline

该接口的唯一实现类DefaultChannelPipeline,该类有HeadContext 和TailContext属性,分别执行责任链的头和尾节点。

1.4 上下文ChannelHandlerContext

该接口有一个抽象类实现AbstractChannelHandlerContext,这个类有prev和next属性,指向责任链的前一个节点和后一个节点。整个责任链是双链表结构。

该接口的handler() 方法返回Context持有的责任处理器handler。

1.5 责任终止机制

  • 在pipeline中任意一个节点,只要我们不手动的往下传播,这个事件就会终止传播在当前节点
  • 对于入站数据,默认会传递到尾结点,进行回收,如果我们不进行下一步传播,事件就会终止在当前节点,别忘记收回msg
  • 对于出站数据,用Header节点的使用Unsafe对象,把数据写回客户端也意味着终止

2、Netty中责任链如何运作?

2.1 我们看一个Netty源码的bind方法样例

我们看一下ServerBootstrap.doBind()方法,所有的bind最终转到这里。继续进入doBind0,如图:

进入下图的channel.bind

2.2 AbstractChannel#bind,调用pipeline,这是责任链模式

2.3 继续进入,我们来到AbstractChannelHandlerContext#bind

看下面红圈的代码,findContextOutbound这行代码用来计算下一步执行责任链上哪一个节点,通过这种方式把责任链执行连起来,后续继续执行就可以,handler的方法都是这样结构的

2.4 掩码MASK_BIND(责任链节点集合,不同责任链执行不同节点)

这两个常量来自下面这个类,我们看到还有很多其他常量,这里表示不同的事件,每个事件占一位,一共16位。

MASK_ONLY_OUTBOUND是出站事件组合出来的,表示出站需要执行的责任节点。不同的掩码表示不同业务的责任链节点集合。

2.5 寻找责任链下一个节点AbstractChannelHandlerContext#findContextInbound

下图是寻找责任链节点的方法,先获取当前节点的下一个节点,然后判断下一个节点是否要执行,不需要的话继续下一个,直到找到需要执行的节点。

这里从一个节点走向下一个节点,直到走完真个责任链。

我们继续看while循环的判断条款skipContext,Context使用传入的mask,也就是MASK_BIND和MASK_ONLY_OUTBOUND做与运算,如果是0的话,说明MASK_ONLY_OUTBOUND不包含绑定操作,该节点不执行,跳过

2.6 执行handler

找到要执行的责任节点后,根据handler类型执行不同的handler的bind方法完成绑定

3、总结:

Netty通过责任链的设计模式是的对事件的处理,符合开闭原则,对新增处理事件开放,对修改责任链逻辑封闭。

将责任处理器分为入站出站的结构很清晰,使用MASK对Handler分类处理,实现了一个轻量级的分类管理,也加快了处理速度。


杜若
70 声望3 粉丝