美文网首页
JVM-Sandbox笔记 -- 事件监听的实现

JVM-Sandbox笔记 -- 事件监听的实现

作者: rock_fish | 来源:发表于2019-11-13 15:14 被阅读0次
    目标
    • 理解事件的种类,调用顺序
    • 理解事件链路 事件与通知的转换 以及方法链和通知栈的关系。
    • 理解流程跳转的控制-通过自定义异常来实现流程的跳转。
    • 理解流程跳转后,通知的有效性。
    事件处理器
    • 概述

    代码中的new一个Listener,sandbox内部就创建并注册一个与之对应的事件处理器。
    Spy的静态方法中将方法事件交给了事件处理器经过一些内部处理,最终回调listener的方法。

    • 注册事件处理器
      注册过程在com.alibaba.jvm.sandbox.core.enhance.weaver.EventListenerHandlers#active中。
      先说明listenerId是什么:listener对象 放入对象池后,得到 listenerId ,程序中不需要操作listener实例,但是上下文中又需要用到listener的引用的地方都用listenerId,;
    public void active(final int listenerId,
            final EventListener listener,
            final Event.Type[] eventTypes) {
            //对于一个listener对象,创建一个事件处理器并与之关联
            //mappingOfEventProcessor 容器中通过listenerId与EventProcessor 建立关联。
            //sandbox源码中注入的方法参数里 有 listenerid,mappingOfEventProcessor通过listenerId能找到事件处理器
            mappingOfEventProcessor.put(listenerId, new EventProcessor(listenerId, listener, eventTypes));
            logger.info("activated listener[id={};target={};] event={}",
                    listenerId,
                    listener,
                    join(eventTypes, ",")
            );
        }
    
    • 获取事件处理器
      mappingOfEventProcessor.get(listenerId);

    • 事件处理器的功能

      1. 提供方法对应的事件对象
      2. 维护事件对应的方法之间的调用关系
      3. 跳转控制

    1和2 是由处理单元来负责

    • 处理单元
      一个监听的方法链路在一个线程中对应着一个处理单元com.alibaba.jvm.sandbox.core.enhance.weaver.EventProcessor.Process。 方法肯定是在多个线程中执行,事件处理器通过ThreadLocal<Process>的方法每个线程创建了一个处理单元Process的实例,这样处理单元之间就是线程隔离的,其内部创建的对象就是线程安全的。
      两个基本的功能
      1. 提供方法对应的事件对象
        EventProcessor.ProcessSingleEventFactory 事件工厂负责创建各种事件对象,比如makeBeforeEvent就创建一个before事件对象;每个事件都是一个单例,下一个事件到来时,其内部通过unsafe方法来给对象的属性变更值,避免了每次创建新对象,减少新生代GC的工作量。
      2. .weaver.EventProcessor.Processstack 负责维护方法之间的调用关系,跟JVM的操作类似(JVM通过栈结构来组织方法的调用关系,调用方法时方法栈帧压栈,方法执行结束后就方法栈帧出栈);栈的深度取决于监听的方法链路的深度。
        在处理链路的时候有两个id很关键,processIdinvokeId,每个方法的before事件即方法的入口处对应一个invokeId,对于方法链路来说,最外层的方法是的invokeId就标识了整个调用链路,即整个执行过程,存储为processId.内部方法的processId就是最外层方法的InvokeId.

    xxxEvent对象是线程复用的,这样在用户使用时就有很多约束,所以处理单元所提供的方法事件,仅在sandbox内部使用,在用户视角的监听器里回调方法中就被封装成了Advice通知对象com.alibaba.jvm.sandbox.api.listener.ext.Advice
    方法之间的调用栈关系,转换为com.alibaba.jvm.sandbox.api.listener.ext.AdviceAdapterListener.OpStack同时也在Advice上体现,通过top属性来指向栈底Advice,通过parent属性指向上一个Advice(朝栈底方向)。Advice更重要的是提供了打标marks和挂载数据attatchment的功能;这样在通知栈中就增加了交互通信的手段。类似于传话的功能。

    • Advice压栈和出栈


      image.png

    report 方法的三个事件

    1. BEFORE
      在BEFORE事件中,sandbox内部通过makeBeforeEvent创建beforeEvent对象,根据这个Event创建了Advice后压栈;后续RETURN/THROWS中使用;所以一个方法内只在入口处创建一个advice对象。
    2. RETURN
      sandbox内部通过makeReturnEvent创建returnEvent对象。
      通过invokeId,从栈顶拿到Advice对象,将事件包裹的函数返回值 放置到 advice中。
    3. THROWS
      sandbox内部通过makeThrowsEvent创建throwsEvent对象。
      通过invokeId,从栈顶拿到Advice对象,将事件包裹的函数返回值 放置到 advice中。

    report 方法中的call事件

    1. CALL_BEFORE
      xxxAdvice对象在一次方法的调用内部只有一个,在before事件中创建。其他的所有事件中复用此通知对象。
      call_before事件中也是通过invokeId,从栈顶拿到Advice对象,复用此advice中;
      call事件中创建了CallTarget 对象,记录了被call方法的一些信息;
      作为attach附加到xxxAdvice上。
    wrapAdvice.attach(target = new CallTarget(
                            cbEvent.lineNumber,//代码行号
                            toJavaClassName(cbEvent.owner),//调用者的类名
                            cbEvent.name,//被调方法名
                            cbEvent.desc//被调方法的参数
                    ));
    

    之后调用 adviceListener.beforeCall

    1. CALL_RETURN
      也是通过invokeId,从栈顶拿到Advice对象,复用此advice中;
      CallTarget target = wrapAdvice.attachment();
      从target中拿到方法名和调用者类名信息;
      之后调用adviceListener.afterCallReturning
      adviceListener.afterCall

    2. CALL_THROWS

    也是通过invokeId,从栈顶拿到Advice对象,复用此advice中;
    CallTarget target = wrapAdvice.attachment();
    从target中拿到方法名和调用者类名信息;
    之后调用adviceListener.afterCallThrowing
    adviceListener.afterCall

    report 方法中的 LINE事件

    也是通过invokeId,从栈顶拿到Advice对象,复用此advice中;
    之后调用adviceListener.beforeLine

    • 函数的嵌套调用,即多层调用的情况
    image.png
    1. 子函数的invokeId 在自增变化
    2. advice的parent 和 top 能查找到整个通知栈的advice
    3. 再通过advice上的attachment可以在上下游之间传递信息。不用再额外的设计和引入其他的载体容器。

    相关文章

      网友评论

          本文标题:JVM-Sandbox笔记 -- 事件监听的实现

          本文链接:https://www.haomeiwen.com/subject/dqghictx.html