Alibaba之jvm-sandbox初体验

news/2024/5/20 0:19:11/文章来源:https://blog.csdn.net/zb_3Dmax/article/details/126636017

前言

在开始之前,我们先来模拟一下以下的场景:
小李:“小明,你的接口没有返回数据,麻烦帮忙看一下?”
小明:“我这边的数据也是从别人的服务器中拿到的,但是我不确定是因为逻辑处理有问题导致没有结果,还是因为我依赖的服务有问题而没有返回结果,我需要确认一下。”
小明:“哎呀,线上没有日志,我需要加个日志上个线。”
30 分钟之后……
小明:“不好意思,日志加错地方了……稍等……”

接来下隆重登场的就是本文的主角 JVM SandBox 了。基于 JVM SandBox,我们可以很容易地做到在不重新部署应用的情况下,给指定的某些类的某些方法加上日志功能。当然,动态加日志仅仅是 JVM SandBox 可以应用的一个小小的场景,JVM SandBox 的威力远不在于此。套用官方的话说就是"JVM-SANDBOX还能帮助你做很多很多,取决于你的脑洞有多大了。"

JVM SandBox 简介

AOP

在介绍 JVM SandBox 之前,我们先来回顾一下 AOP 技术。

AOP(面向切面编程,Aspect Oriented Programming)技术已被业界广泛应用,其思想是面向业务处理过程的某个步骤或阶段进行编程,这个步骤或阶段被称为切面,其目的是降低业务逻辑的各部分之间的耦合,常见的 AOP 实现基本原理有两种:代理和行为注入。

1)代理模式

在代理模式下,我们会创建一个代理对象来代理原对象的行为,代理对象拥有原对象行为执行的控制权,在这种模式下,我们基于代理对象在原对象行为执行的前后插入代码来实现 AOP。
代理模式.png

2)行为注入模式

在行为注入模式下,我们不会创建一个新的对象,而是修改原对象,在原对象行为的执行前后注入代码来实现 AOP。
行为注入模式.png

JVM SandBox

JVM SandBox 是阿里开源的一款 JVM 平台非侵入式运行期 AOP 解决方案,本质上是一种 AOP 落地形式。那么可能有同学会问:已有成熟的 Spring AOP 解决方案,阿里巴巴为什么还要“重复造轮子”?这个问题要回到 JVM SandBox 诞生的背景中来回答。在 2016 年中,天猫双十一催动了阿里巴巴内部大量业务系统的改动,恰逢徐冬晨(阿里巴巴测试开发专家)所在的团队调整,测试资源保障严重不足,迫使他们必须考虑更精准、更便捷的老业务测试回归验证方案。开发团队面临的是新接手的老系统,老的业务代码架构难以满足可测性的要求,很多现有测试框架也无法应用到老的业务系统架构中,于是需要新的测试思路和测试框架。

为什么不采用 Spring AOP 方案呢?Spring AOP 方案的痛点在于不是所有业务代码都托管在 Spring 容器中,而且更底层的中间件代码、三方包代码无法纳入到回归测试范围,更糟糕的是测试框架会引入自身所依赖的类库,经常与业务代码的类库产生冲突,因此,JVM SandBox 应运而生。

JVM SandBox 本身是基于插件化的设计思想,允许用于以“模块”的方式基于 JVM SandBox 提供的 AOP 能力开发新的功能。基于 JVM SandBox,我们不需要关心如何在 JVM 层实现 AOP 的技术细节,只需要通过 JVM SandBox 提供的编程结构告诉“沙箱”,我们希望对哪些类哪些方法进行 AOP,在切面点做什么即可,JVM SandBox 模块功能编写起来非常简单。

JVM-SANDBOX(沙箱)实现了一种在不重启、不侵入目标JVM应用的AOP解决方案

沙箱的特性
  • 无侵入:目标应用无需重启也无需感知沙箱的存在
  • 类隔离:沙箱以及沙箱的模块不会和目标应用的类相互干扰
  • 可插拔:沙箱以及沙箱的模块可以随时加载和卸载,不会在目标应用留下痕迹
  • 多租户:目标应用可以同时挂载不同租户下的沙箱并独立控制
  • 高兼容:支持JDK[6,11]
实时无侵入AOP框架

在常见的AOP框架实现方案中,有静态编织和动态编织两种。

静态编织:静态编织发生在字节码生成时根据一定框架的规则提前将AOP字节码插入到目标类和方法中,实现AOP;
动态编织:动态编织则允许在JVM运行过程中完成指定方法的AOP字节码增强.常见的动态编织方案大多采用重命名原有方法,再新建一个同签名的方法来做代理的工作模式来完成AOP的功能(常见的实现方案如CgLib),但这种方式存在一些应用边界:

  • 侵入性:对被代理的目标类需要进行侵入式改造。比如:在Spring中必须是托管于Spring容器中的Bean
  • 固化性:目标代理方法在启动之后即固化,无法重新对一个已有方法进行AOP增强

要解决无侵入的特性需要AOP框架具备 在运行时完成目标方法的增强和替换。在JDK的规范中运行期重定义一个类必须准循以下原则

  • 不允许新增、修改和删除成员变量
  • 不允许新增和删除方法
  • 不允许修改方法签名

JVM-SANDBOX属于基于Instrumentation的动态编织类的AOP框架,通过精心构造了字节码增强逻辑,使得沙箱的模块能在不违反JDK约束情况下实现对目标应用方法的无侵入运行时AOP拦截。

核心原理

事件驱动

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

// BEFORE
try {/** do something...*/// RETURNreturn;} catch (Throwable cause) {// THROWS
}

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

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

类隔离策略

沙箱通过自定义的SandboxClassLoader破坏了双亲委派的约定,实现了和目标应用的类隔离。所以不用担心加载沙箱会引起应用的类污染、冲突。各模块之间类通过ModuleJarClassLoader实现了各自的独立,达到模块之间、模块和沙箱之间、模块和应用之间互不干扰
jvm-sandbox-classloader.png

类增强策略

沙箱通过在BootstrapClassLoader中埋藏的Spy类完成目标类和沙箱内核的通讯

jvm-sandbox-enhance-class.jpg

快速安装

下载并安装,开箱即用

# 下载最新版本的JVM-SANDBOX
wget http://ompc.oss-cn-hangzhou.aliyuncs.com/jvm-sandbox/release/sandbox-stable-bin.zip# 解压
unzip sandbox-stable-bin.zip

挂载目标应用

# 进入沙箱执行脚本
cd sandbox/bin# 目标JVM进程21815 
./sandbox.sh -p 21815 

挂载成功后会提示

# ./sandbox.sh -p 21815 NAMESPACE : defaultVERSION : 1.2.1MODE : ATTACHSERVER_ADDR : 0.0.0.0SERVER_PORT : 42641UNSAFE_SUPPORT : ENABLESANDBOX_HOME : /root/jvm_sandbox_demo/sandbox/bin/..SYSTEM_MODULE_LIB : /root/jvm_sandbox_demo/sandbox/bin/../moduleUSER_MODULE_LIB : /root/jvm_sandbox_demo/sandbox/sandbox-module;~/.sandbox-module;SYSTEM_PROVIDER_LIB : /root/jvm_sandbox_demo/sandbox/bin/../providerEVENT_POOL_SUPPORT : DISABLE

卸载沙箱

./sandbox.sh -p 21815 -S
jvm-sandbox[default] shutdown finished.

还有一些指令,建议查阅官方提供的文档或者查阅 /sandbox/bin/sandbox.sh内的指令内容

实例

线上发生文件上传故障,异常错误提示文件存储路径异常,目的是通过jvm-sandbox在不破坏不重启的前提下获知参数中的文件存储路径是否与配置文件一致
先上代码

package com.cn;import com.alibaba.jvm.sandbox.api.Information;
import com.alibaba.jvm.sandbox.api.Module;
import com.alibaba.jvm.sandbox.api.annotation.Command;
import com.alibaba.jvm.sandbox.api.listener.ext.Advice;
import com.alibaba.jvm.sandbox.api.listener.ext.AdviceListener;
import com.alibaba.jvm.sandbox.api.listener.ext.EventWatchBuilder;
import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher;
import org.kohsuke.MetaInfServices;import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.logging.Logger;@MetaInfServices(Module.class)
@Information(id = "my-sandbox-module")// 模块名,在指定挂载进程后通过-d指定模块,配合@Command注解来唯一确定方法
public class MySandBoxModule implements Module {//日志输出,默认采用logback,这里的日志输出到切入的服务日志中private Logger LOG = Logger.getLogger(MySandBoxModule.class.getName());@Resourceprivate ModuleEventWatcher moduleEventWatcher;@Command("addLog")// 模块命令名public void addLog() {new EventWatchBuilder(moduleEventWatcher).onClass("cn.com.service.impl.PackageServiceImpl")// 想要对 PackageServiceImpl 这个类进行切面.onBehavior("bathSave")// 想要对上面类的 bathSave 方法进行切面.onWatch(new AdviceListener() {//对方法执行之前执行@Overrideprotected void before(Advice advice) throws Throwable {//获取方法的所有参数Object[] parameterArray = advice.getParameterArray();if (parameterArray != null) {for (Object po : parameterArray) {//方法参数可能为空,规避报错if (po != null) {/***  目标方法*  public List<UploadResult> bathSave(List<Package> pkgs, MultipartFile[] files, String hdfsDir) {}**  这里只关心MultipartFile参数,其余参数过滤**  po.getClass() 输出内容为 class [Lorg.springframework.web.multipart.MultipartFile;*  po.getClass().getName() 输出内容为[Lorg.springframework.web.multipart.MultipartFile**  最开始的设想是直接cast转型,但是由于类加载器不同,是不行的,所以最好反射来操作*/if (po.getClass().getName().contains("MultipartFile")) {//目标方法参数是一个MultipartFile[]Object[] o2 = (Object[]) po;for (Object o3 : o2) {Field ff = o3.getClass().getDeclaredField("part");ff.setAccessible(true);Object part = ff.get(o3);Field ff1 = part.getClass().getDeclaredField("location");ff1.setAccessible(true);Object file = ff1.get(part);Field ff11 = file.getClass().getDeclaredField("path");ff11.setAccessible(true);//最后的结果输出:打印的路径-------/app/upload,符合预期LOG.info("打印的路径-------" + ff11.get(file));}}}}}}});}
}

部署

打包

mvn clean package

上传或者复制到目录

/sandbox/sandbox-module
# 效果如下
/sandbox/sandbox-module/my-sandbox-module-1.0-SNAPSHOT-jar-with-dependencies.jar

挂载到对应进程

# my-sandbox-module就是类上的模块名,addLog方法上的模块命令名
./sandbox.sh -p 21815 -d 'my-sandbox-module/addLog'

挂载之后可以通过指令查看是否挂载成功

# ./sandbox.sh -p 4432 -l
my-sandbox-module   	ACTIVE  	LOADED  	1    	1    	UNKNOW_VERSION 	UNKNOW_AUTHOR
sandbox-info        	ACTIVE  	LOADED  	0    	0    	0.0.4          	luanjia@taobao.com
broken-clock-tinker 	ACTIVE  	LOADED  	0    	0    	UNKNOW_VERSION 	UNKNOW_AUTHOR
sandbox-module-mgr  	ACTIVE  	LOADED  	0    	0    	0.0.2          	luanjia@taobao.com
sandbox-control     	ACTIVE  	LOADED  	0    	0    	0.0.3          	luanjia@taobao.com
total=5

这时执行对应的api在挂载的服务日志中就能查看通过沙箱添加的日志了。

sandbox的日志路径可以在**/sandbox/cfg/sandbox-logback.xml**文件中进行配置(sandbox日志主要输出挂载信息,以及沙箱程序的异常错误信息)

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="10000"><appender name="SANDBOX-FILE-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${user.home}/logs/sandbox/sandbox.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><FileNamePattern>${user.home}/logs/sandbox/sandbox.log.%d{yyyy-MM-dd}</FileNamePattern><MaxHistory>30</MaxHistory></rollingPolicy><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} %SANDBOX_NAMESPACE %-5level %msg%n</pattern><charset>UTF-8</charset></encoder></appender><root level="info"><appender-ref ref="SANDBOX-FILE-APPENDER"/></root>
</configuration>

以上就是对Alibaba的jvm-sandbox的初体验,其他更丰富的场景还有待进一步体验

参考文档

1.https://www.infoq.cn/article/TSY4lGjvSfwEuXEBW*Gp
2.https://github.com/alibaba/jvm-sandbox

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_381462.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

alluxio简单使用

alluxio简单使用 本文是基于alluxio官网和自己实践整理。 Alluxio版本&#xff1a;1.8.1CDH 1.15.2 1、介绍 以内存为中心的分布式虚拟存储系统。Alluxio在上层计算框架和底层存储系统之间架起了桥梁&#xff0c;应用层只需要访问Alluxio即可以访问底层对接了的任意存储系统的…

Oracle索引详解

索引 类似于书的目录&#xff0c;提高查询效率。 创建索引语法&#xff1a; CREATE [UNIQUE] [BITMAP] INDEX 索引名称 ON 表名(字段,[字段,..,..]);名词解释&#xff1a;UNIQUE 唯一索引BITMAP 位图索引默认不写 UNIQUE 和 BITMAP 为普通索引表名后面写多个字段为复合索引在字…

activeMQ、rabbitMQ学习对比心得

一、activemq activemq工作模型比较简单。只有两种模式 queue、topics 。 queue就多对一&#xff0c;producer往queue里发送消息&#xff0c;消费者从queue里取&#xff0c;消费一条&#xff0c;就从queue里移除一条。如果一个消费者消费速度不够快怎么办呢&#xff1f;在act…

About-Flink

About-Flink 一、Flink简介 1.1、flink特点1.2、分层Api1.3、Flink vs Spark Streaming 二、Flink批处理应用 2.1、依赖的引入2.2、准备批处理文件2.3、wordCount编码2.4、自定义类 三、Flink流处理应用 3.1、wordCount编码3.2、设置并行度-默认为43.2、数据来源socket3.3、配…

通过 replace() 和正则实现 将文本中的所有数字颜色高亮

实现的效果&#xff1a; 用到的知识点&#xff1a; replace() 方法用于在字符串中用一些字符替换另一些字符&#xff0c;或替换一个与正则表达式匹配的子串。 repalce&#xff08; a, b &#xff09; 必须传两个值&#xff0c;其中a 是要替换的文本&#xff0c;或者满足条件…

javaweb JAVA JSP球鞋销售系统购物系统ssm购物系统购物商城系统源码(ssm电子商务系统)

JSP球鞋销售系统购物系统ssm购物系统购物商城系统源码&#xff08;ssm电子商务系统&#xff09;

生产和同城存储双活架构下,发生脑裂问题影响数据库读写,如何快速分析问题和解决问题?

数据中心脑裂问题,简单说就是两个数据中心间的网络和存储链路同时发生中断,导致两个数据中心内的应用、数据库或者操作系统同时抢占和利用共享的资源,造成资源的数据不一致,产生重大影响。如何避免脑裂是每个存储双活方案都需要尤为重视的问题,脑裂会带来长时间的存储读写…

linux上redis单机的安装

1. 官网下载 https://github.com/redis/redis/archive/7.0.4.tar.gz 2. 上传到虚拟机/data/目录下、解压 tar -xzvf redis-7.0.4.tar.gz 3. 进入redis-7.0.4此目录 cd redis-7.0.4;ll 4. 安装到指定目录中 a. mkdir /usr/local/redis b. make PREFIX/usr/local/redis inst…

沃尔玛、eBay、wish、新蛋等美系平台对于测评风控点有哪些?怎么解决

很多人把各大平台风控想得过于简单&#xff0c;以为注册一批买家账号配一个IP就能进行下单上评&#xff0c;这也是导致市面上的测评现象杂乱无章。但是一定要明白一点各大电商平台都是一家数据公司他的算法一定是根据市场的变化而不断调整的。 平台检测的方式有很多种 1、平台…

RabbitMQ入门(二)

1.概述 RabbityMQ整体上是一个生产者和消费者模式。生产者生产消息到消息中间件的服务节点&#xff08;Broker&#xff09;,服务节点中包含交换器&#xff08;Exchange&#xff09;和队列&#xff08;Queue&#xff09;&#xff0c;生产的消息首先经过交换器&#xff0c;再由交…

搭建vue3项目

搭建vue3项目搭建准备创建项目选择所需配置运行项目vue3已经被大众所熟悉&#xff0c;很多公司都在做vue2到vue3的升级。 介绍vue3项目的搭建过程 搭建准备 前端开发环境需要node.js&npm node下载地址:http://nodejs.cn/download/ 根据自己电脑环境下载就行 安装vue-cli3…

2022/08/31 day14:企业级解决方案

文章目录目录缓存预热缓存雪崩缓存击穿缓存穿透性能指标监控总结目录 面试问题 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EtBtkGNE-1661933471760)(en-resource://database/5507:1)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下…

抖音小程序模板全行业整理合集,抖音小程序制作平台分享

小弟我是来自第三方抖音小程序制作平台的打工人&#xff0c;给大家整合了一些我们平台的抖音小程序模板&#xff0c;大家可以根据需要来获取。 步骤就是点击下方的链接&#xff0c;选好自己的抖音小程序模板&#xff0c;在平台注册账号直接套用到自己的抖音小程序上&#xff0…

深入理解蓝牙BLE之“信道管理”

目录 一.BLE的调制解调&#xff1a; 二.BLE的信道&#xff1a; 三.BLE的广播信道&#xff1a; 四.BLE的数据信道&#xff1a; 五.BLE信道管理&#xff1a; 5.1广播信道的随机延时&#xff1a; 5.2数据信道的调频算法&#xff1a; 跳频算法1&#xff1a; 跳频算法2&…

02.Haoop 虚拟机 桥接与NAT之间区别 及桥接设置

首先说 我的硬件准备&#xff0c;1台windows系统&#xff0c;1台mac pro 。 在 物理机上使用了 VMWARE CENTOS 7 的 方式进行配置。 那么我希望能实现把 这2台机器连在一起&#xff0c;做Hadoop 的集群。 网络问题是首先需要解决的事情&#xff0c;主要不通物理主机之间一直…

02:入门及安装(狂神说RabbitMQ)

RabbitMQ入门及安装 https://www.bilibili.com/video/BV1dX4y1V73Gp27 概述 简单概述&#xff1a; RabbitMQ是一个开源的遵循 AMQP协议实现的基于 Erlang语言编写&#xff0c;支持多种客户端&#xff08;语言&#xff09;&#xff0c;用于在分布式系统中存储消息&#xff0…

Spring Security 入门之自定义表单登录开发实现(三)

文章目录1. 前言2. 自定义认证2.1 自定义登录页面2.2 后端认证逻辑3. 自定义登陆成功处理3.1 登陆成功原理3.2 自定义登陆成功响应处理4. 自定义登陆失败处理4.1 登陆失败原理4.2 自定义登陆失败响应处理5. 注销用户处理5.1 注销原理总结1. 前言 在弄懂HelloWorld案例后&#…

Node.js | 使用内置模块 event 实现发布订阅模式

&#x1f5a5;️ NodeJS专栏&#xff1a;Node.js从入门到精通 &#x1f5a5;️ 蓝桥杯真题解析&#xff1a;蓝桥杯Web国赛真题解析 &#x1f9e7; 加入社区领红包&#xff1a;海底烧烤店ai&#xff08;从前端到全栈&#xff09; &#x1f9d1;‍&#x1f4bc;个人简介&#xff…

自动化测试中的验证码问题

做自动化测试的同学在面试的时候经常会遇到这问题&#xff0c;而且我们在实际的工作中也会遇到这个问题&#xff0c;那么这问题到底该怎么处理&#xff1f; 下面给出了面试过程中常见的相关面试题供大家参考&#xff1a; 01 在做自动化登陆的同时&#xff0c;如何绕过验证码&a…

windows下安装docker

下载docker&#xff0c;通过Redirecting…这个下载docker 正在上传…重新上传取消 下载完安装 安装完成后&#xff0c;进入powershell&#xff0c;输入命令docker network ls,查看docker网络&#xff0c;如果没有bridge项目&#xff0c;创建容器会报错(Windows容器就是两…