【alibaba/jvm-sandbox#05】沙箱事件详解

一、alibaba/jvm-sandbox 概述

alibaba/jvm-sandbox 是 JVM 沙箱容器,一种 JVM 的非侵入式运行期 AOP 解决方案。沙箱容器提供

  1. 动态增强类你所指定的类,获取你想要的参数和行信息甚至改变方法执行
  2. 动态可插拔容器框架

在其能力至上构建的上层应用有:

《【alibaba/jvm-sandbox#02】通过无侵入 AOP 实现行为注入和流控》 介绍了 JVM-SANDBOX 属于基于 Instrumentation 的动态编织类的 AOP 框架,通过精心构造了字节码增强逻辑,使得沙箱的模块能在不违反 JDK 约束情况下实现对目标应用方法的无侵入运行时 AOP 拦截。实现行为注入和流控。

本篇的目标是加深对其沙箱事件机制的理解

二、回顾沙箱事件驱动

在沙箱的世界观中,任何一个Java方法的调用都可以分解为BEFORERETURNTHROWS三个环节,由此在三个环节上引申出对应环节的事件探测和流程控制机制。

// BEFORE
try {

   /*
    * do something...
    */

    // RETURN
    return;

} catch (Throwable cause) {
    // THROWS
}
复制代码

基于BEFORERETURNTHROWS三个环节事件分离,沙箱的模块可以完成很多类AOP的操作。

  1. 可以感知和改变方法调用的入参

  2. 可以感知和改变方法调用返回值和抛出的异常

  3. 可以改变方法执行的流程

    • 在方法体执行之前直接返回自定义结果对象,原有方法代码将不会被执行
    • 在方法体返回之前重新构造新的结果对象,甚至可以改变为抛出异常
    • 在方法体抛出异常之后重新抛出新的异常,甚至可以改变为正常返回

这个描述看起来很容易理解,但实现以上功能,如此对事件进行描述是不完整的。

三、沙箱事件详解

在JVM-Sandbox的世界观中,任何一个Java方法的调用都可以分解为BEFORERETURNTHROWS三个环节,由此在三个环节上引申出对应环节的事件探测流程控制机制

  • BEFORE事件:执行方法体之前被调用
  • RETURN事件:执行方法体返回之前被调用
  • THROWS事件:执行方法体抛出异常之前被调用

为了记录代码调用行记录,增加了一个LineEvent

  • LINE事件:方法行被执行后调用,目前仅记录行号

CALL事件系列是从GREYS中衍生过来的事件,它描述了一个方法内部,调用其他方法的过程。整个过程可以被描述成为三个阶段

  • CALL_BEFORE事件:一个方法被调用之前
  • CALL_RETURN事件:一个方法被调用正常返回之后
  • CALL_THROWS事件:一个方法被调用抛出异常之后

监听foo方法的BEFORE、RETURN、THROWS、LINE、CALL_BEFORE、CALL_RETURN、CALL_THROWS事件

void foo(){
	// BEFORE-EVENT
	try {

   		/*
   	 	* do something...
   	 	*/
    	try{
    	    //LINE-EVENT
    	    //CALL_BEFORE-EVENT
    		a();
    		//CALL_RETURN-EVENT
    	} catch (Throwable cause) {
    		// CALL_THROWS-EVENT
		}
		//LiNE-EVENT
    	// RETURN-EVENT
    	return;

	} catch (Throwable cause) {
    	// THROWS-EVENT
	}
}
复制代码

需要特别注意、注意、注意:IMMEDIATELY_RETURNIMMEDIATELY_THROWS不是事件,他们是流程控制机制,由com.alibaba.jvm.sandbox.api.ProcessControlExceptionthrowReturnImmediately(Object)throwThrowsImmediately(Throwable)触发,完成对方法的流程控制

  • IMMEDIATELY_RETURN:立即调用:RETURN事件
  • IMMEDIATELY_THROWS:立即调用:THROWS事件

整体的流转控制如下图:


                                        +-------+
                                        |       |
 +========+  <return>             +========+    | <return immediately>
 |        |  <return immediately> |        |    |
 | BEFORE |---------------------->| RETURN |<---+
 |        |                       |        |
 +========+                       +========+
     |                              |    ^
     |         <throws immediately> |    |
     |                              |    | <return immediately>
     |                              v    |
     |                            +========+
     |                            |        |
     +--------------------------->| THROWS |<---+
                    <throws>      |        |    |
        <throws immediately>      +========+    | <throws immediately>
                                        |       |
                                        +-------+
复制代码

四、避免多次增强

Sandbox 支持对于同一个类、同一个方法多次增强,增强之间依然需满足其原定逻辑流转。但官方说不建议多次增强,为什么呢?

下图是两次增强 处理逻辑图如下,每增强一次监听指令的数量指数级增长。

返回状态(0:NONE;1:RETURN;2:THROWS)

  • RET = 1 的时候,这个方法需要立即返回,后面的都不执行了
  • RET = 0 的时候,这个方法继续向下执行
  • RET = 2 的时候,这个方法立即抛出异常

从这个逻辑的复杂度上能直观感受到多次增强后事件以及流控的复杂度是大大的增加了。如果多次松散的增强,遇到非预期结果时,排查效率也很低。所以对同一个类的增强控制,尽量收敛到一个逻辑中,这就要求在使用层面做好设计与规范。

五、最后说一句

我是石页兄,如果这篇文章对您有帮助,或者有所启发的话,欢迎关注笔者的微信公众号【 架构染色 】进行交流和学习。您的支持是我坚持写作最大的动力。


原文链接:https://juejin.cn/post/7182839519433981989
来源:稀土掘金

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值