• Sandbox 入门(打包、安装、启动、调试、日志)


    简介

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

    沙箱的特性

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

    沙箱常见应用场景

    • 线上故障定位
    • 线上系统流控
    • 线上故障模拟
    • 方法请求录制和结果回放
    • 动态日志打印
    • 安全信息监测和脱敏

    JVM-SANDBOX还能帮助你做很多很多,取决于你的脑洞有多大了。

    实时无侵入AOP框架

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

    1. 静态编织:静态编织发生在字节码生成时根据一定框架的规则提前将AOP字节码插入到目标类和方法中,实现AOP;
    2. 动态编织:动态编织则允许在JVM运行过程中完成指定方法的AOP字节码增强.常见的动态编织方案大多采用重命名原有方法,再新建一个同签名的方法来做代理的工作模式来完成AOP的功能(常见的实现方案如CgLib),但这种方式存在一些应用边界:
      • 侵入性:对被代理的目标类需要进行侵入式改造。比如:在Spring中必须是托管于Spring容器中的Bean。
      • 固化性:目标代理方法在启动之后即固化,无法重新对一个已有方法进行AOP增强。

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

    1. 不允许新增、修改和删除成员变量
    2. 不允许新增和删除方法
    3. 不允许修改方法签名

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

    源码

    源码下载-一定要切换到稳定版本:
    https://github.com/alibaba/jvm-sandbox
    也可以直接下载打包好的版本
    https://github.com/alibaba/jvm-sandbox/releases/tag/1.3.3

    安装

    源码包导入开发工具后切换到bin目录:

    image.png

    1. 注释掉不需要的插件:
    
    <plugin>
        <groupId>org.sonatype.pluginsgroupId>
        <artifactId>nexus-staging-maven-pluginartifactId>
        <version>1.6.7version>
        <extensions>trueextensions>
        <configuration>
            <serverId>luanjia-ossrhserverId>
            <nexusUrl>https://oss.sonatype.org/nexusUrl>
            <autoReleaseAfterClose>trueautoReleaseAfterClose>
        configuration>
    plugin>
    <plugin>
        <groupId>org.apache.maven.pluginsgroupId>
        <artifactId>maven-gpg-pluginartifactId>
        <version>1.5version>
        <executions>
            <execution>
                <id>sign-artifactsid>
                <phase>verifyphase>
                <goals>
                    <goal>signgoal>
                goals>
            execution>
        executions>
    plugin>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    1. 打包
      打包前先将sandbox-packages.sh脚本中执行单测的代码调过,修改如下:
    # maven package the sandbox
    mvn clean cobertura:cobertura package -Dmaven.test.skip=true -f ../pom.xml \
        || exit_on_err 1 "package sandbox failed."
    
    • 1
    • 2
    • 3

    修改完成后执行脚本./sandbox-packages.sh ,脚本执行后会在工程target目录下生成安装好的文件,如图:

    image.png

    sandbox-stable-bin.zip就是生成好的安装文件,如果需要在哪里安装直接将这个包在那个服务器解压即可。

    脚本会将打包后的文件拷贝到安装目录:

    # copy jar to TARGET_DIR
    cp ../sandbox-core/target/sandbox-core-*-jar-with-dependencies.jar ${SANDBOX_TARGET_DIR}/lib/sandbox-core.jar \
        && cp ../sandbox-agent/target/sandbox-agent-*-jar-with-dependencies.jar ${SANDBOX_TARGET_DIR}/lib/sandbox-agent.jar \
        && cp ../sandbox-spy/target/sandbox-spy-*-jar-with-dependencies.jar ${SANDBOX_TARGET_DIR}/lib/sandbox-spy.jar \
        && cp sandbox-logback.xml ${SANDBOX_TARGET_DIR}/cfg/sandbox-logback.xml \
        && cp sandbox.properties ${SANDBOX_TARGET_DIR}/cfg/sandbox.properties \
        && cp sandbox.sh ${SANDBOX_TARGET_DIR}/bin/sandbox.sh \
        && cp install-local.sh ${SANDBOX_TARGET_DIR}/install-local.sh
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    目录结构

    ./sandbox/
        +--bin/
        |   +--sandbox.sh
        |
        +--cfg/
        |   +--sandbox-logback.xml
        |   +--sandbox.properties
        |   `--version
        |
        +--lib/
        |   +--sandbox-agent.jar
        |   +--sandbox-core.jar
        |   `--sandbox-spy.jar
        |
        +--provider/
        |.  `--sandbox-mgr-provider.jar
        |
        `--module/
          `--sandbox-mgr-module.jar
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    详细说明可以参考官网:https://github.com/alibaba/jvm-sandbox/wiki/CONFIG

    安装

    方式一直接拷贝打包好的安装文件:

    直接将上面生成的包sandbox-stable-bin.zip拷贝到安装目录,直接解压即可。

    方式二直接网上下载release包:

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

    方式三执行install-local脚本。

    不能直接执行工程bin目录下的install-local.sh脚本,需要执行我们上面打包好的target中的脚本,如图:

    image.png

    执行安装脚本(本地调试用这种方式):

     cd ../target/sandbox
    ./install-local.sh -p /Users/admin/Documents/
    
    • 1
    • 2

    -p:表示安装目录,默认安装在${HOME}/.opt目录下。

    启动

    sandbox启动有两种方式:ATTACHAGENT

    ATTACH方式启动

    即插即用的启动模式,可以在不重启目标JVM的情况下完成沙箱的植入。原理和GREYS、BTrace类似,利用了JVM的Attach机制实现。

    假设目标JVM进程号为’33342’,进入沙箱执行脚本

    cd {安装目录}/sandbox/bin
    
    • 1

    挂载目标应用

    ./sandbox.sh -p 33342
    
    • 1

    挂载成功后会提示

    ./sandbox.sh -p 33342
               NAMESPACE : default
                 VERSION : 1.2.0
                    MODE : ATTACH
             SERVER_ADDR : 0.0.0.0
             SERVER_PORT : 55756
          UNSAFE_SUPPORT : ENABLE
            SANDBOX_HOME : /Users/vlinux/opt/sandbox
       SYSTEM_MODULE_LIB : /Users/vlinux/opt/sandbox/module
         USER_MODULE_LIB : ~/.sandbox-module;
     SYSTEM_PROVIDER_LIB : /Users/vlinux/opt/sandbox/provider
      EVENT_POOL_SUPPORT : DISABLE
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    卸载沙箱

    ./sandbox.sh -p 33342 -S
    jvm-sandbox[default] shutdown finished.
    
    • 1
    • 2

    ./sandbox.sh命令参数详解: https://github.com/alibaba/jvm-sandbox/wiki/CONFIG

    AGENT方式启动

    有些时候我们需要沙箱工作在应用代码加载之前,或者一次性渲染大量的类、加载大量的模块,此时如果用ATTACH方式加载,可能会引起目标JVM的卡顿或停顿(GC),这就需要启用到AGENT的启动方式。

    假设SANDBOX被安装在了/Users/luanjia/pe/sandbox,需要在JVM启动参数中增加上

    -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000 -javaagent:/Users/luanjia/pe/sandbox/lib/sandbox-agent.jar
    
    • 1

    这样沙箱将会伴随着JVM启动而主动启动并加载对应的沙箱模块。

    对比Agent和ATTACH,Agent(premain)是JDK5就提供的,主要提供JVM启动时的代理加载功能,灵活性和易用性欠佳;ATTACH(agentmain)则是JDK6提出的,能够支持JVM运行时的代理启动,灵活性和易用性好,给线上调试等提供了底层能力支持。

    调试

    sandbox的调试需要使用远程调试,在目标应用启动时加上远程调试参数(目标应用必须以非DEBUG模式启动):

    -Xdebug
    -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
    
    • 1
    • 2

    image.png

    使用IDEA的远程调试功能,并加上如下配置:

    -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5050
    
    • 1

    image.png

    sandboxATTACH方式启动即可:

    ./sandbox.sh -p `ps -ef | grep java | grep 'com.alibaba.repeater.console.start.Application' | grep -v grep | awk '{print $2}'`
    
    • 1

    image.png

    日志

    Sandbox的日志

    sandbox的日志配置是在cfg下的sandbox-logback.xml文件。这个文件会在沙箱启动时被加载,通过配置我们可以找到日志文件位置为${user.home}/logs/sandbox/sandbox.log.%d{yyyy-MM-dd}

    
    <configuration scan="true" scanPeriod="10000">
    
        <appender name="SANDBOX-FILE-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${user.home}/logs/sandbox/sandbox.logfile>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <FileNamePattern>${user.home}/logs/sandbox/sandbox.log.%d{yyyy-MM-dd}FileNamePattern>
                <MaxHistory>30MaxHistory>
            rollingPolicy>
            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss} %SANDBOX_NAMESPACE %-5level %msg%npattern>
                <charset>UTF-8charset>
            encoder>
        appender>
    
        <root level="info">
            <appender-ref ref="SANDBOX-FILE-APPENDER"/>
        root>
    
    configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    module 日志

    module本身是一个单独项目(jar),所以module的日志是由项目本身控制,以源码中的sandbox-debug-module为例,他其实是一个module编写的是示例工程,他的日志可以直接查看项目中的logback.xml文件。

    
    <configuration scan="true" scanPeriod="10000">
    
        
        <logger name="DEBUG-SERVLET-ACCESS" level="INFO">
            <appender class="ch.qos.logback.core.rolling.RollingFileAppender">
                <file>${user.home}/logs/sandbox/debug/servlet-access.logfile>
                <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                    <FileNamePattern>${user.home}/logs/sandbox/debug/servlet-access.log.%d{yyyy-MM-dd}FileNamePattern>
                    <MaxHistory>30MaxHistory>
                rollingPolicy>
                <encoder>
                    <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %msg%npattern>
                    <charset>UTF-8charset>
                encoder>
            appender>
        logger>
        
        
        <logger name="DEBUG-EXCEPTION-LOGGER" level="INFO">
            <appender class="ch.qos.logback.core.rolling.RollingFileAppender">
                <file>${user.home}/logs/sandbox/debug/exception-monitor.logfile>
                <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                    <FileNamePattern>${user.home}/logs/sandbox/debug/exception-monitor.log.%d{yyyy-MM-dd}FileNamePattern>
                    <MaxHistory>30MaxHistory>
                rollingPolicy>
                <encoder>
                    <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %msg%npattern>
                    <charset>UTF-8charset>
                encoder>
            appender>
        logger>
    
        
        <logger name="DEBUG-JDBC-LOGGER" level="INFO">
            <appender class="ch.qos.logback.core.rolling.RollingFileAppender">
                <file>${user.home}/logs/sandbox/debug/jdbc-monitor.logfile>
                <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                    <FileNamePattern>${user.home}/logs/sandbox/debug/jdbc-monitor.log.%d{yyyy-MM-dd}FileNamePattern>
                    <MaxHistory>30MaxHistory>
                rollingPolicy>
                <encoder>
                    <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %msg%npattern>
                    <charset>UTF-8charset>
                encoder>
            appender>
        logger>
    
        
        <logger name="DEBUG-SPRING-LOGGER" level="INFO">
            <appender class="ch.qos.logback.core.rolling.RollingFileAppender">
                <file>${user.home}/logs/sandbox/debug/spring-monitor.logfile>
                <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                    <FileNamePattern>${user.home}/logs/sandbox/debug/spring-monitor.log.%d{yyyy-MM-dd}FileNamePattern>
                    <MaxHistory>30MaxHistory>
                rollingPolicy>
                <encoder>
                    <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %msg%npattern>
                    <charset>UTF-8charset>
                encoder>
            appender>
        logger>
    
        
        <root level="INFO">
            <appender class="ch.qos.logback.core.rolling.RollingFileAppender">
                <file>${user.home}/logs/sandbox/debug/debug.logfile>
                <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                    <FileNamePattern>${user.home}/logs/sandbox/debug/debug.log.%d{yyyy-MM-dd}FileNamePattern>
                    <MaxHistory>30MaxHistory>
                rollingPolicy>
                <encoder>
                    <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %msg%npattern>
                    <charset>UTF-8charset>
                encoder>
            appender>
        root>
    
        
        <logger name="DEBUG-LIFECYCLE-LOGGER" level="INFO">
            <appender class="ch.qos.logback.core.rolling.RollingFileAppender">
                <file>${user.home}/logs/sandbox/debug/lifecycle-monitor.logfile>
                <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                    <FileNamePattern>${user.home}/logs/sandbox/debug/lifecycle-monitor.log.%d{yyyy-MM-dd}FileNamePattern>
                    <MaxHistory>30MaxHistory>
                rollingPolicy>
                <encoder>
                    <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %msg%npattern>
                    <charset>UTF-8charset>
                encoder>
            appender>
        logger>
    configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
  • 相关阅读:
    大数据ClickHouse进阶(四):ClickHouse的索引深入了解
    C++之<set和map模拟实现>
    Linux常见命令总结
    ubuntu18.04安装gtsam
    关于MySQL安装时一直卡在starting sever......手把手教你搞定
    如何将两个pdf合并成一个?pdf合并技巧分享
    java基础19
    三、部署kafka
    变电站人员安全定位管理方案介绍
    冷知识:预处理字符串操作符
  • 原文地址:https://blog.csdn.net/xiaolyuh123/article/details/127771250