AOP—JVM SandBox—快速上手
原文作者:stingfire
原文地址:深入學習jvm-sandbox(安裝&快速上手)
目錄
一、安裝
1. 下載
2. 運行安裝腳本
3. 閱讀安裝腳本
3.1 首先是定義安裝目錄變量
3.2 解析參數
3.3 拷貝文件
3.4 更新sandbox.sh腳本
3.5 輸出結果
二、快速上手
2.1 ATTACH方式啟動
2.2 AGENT方式啟動
2.3 實操(wiki例子:修復一個損壞了的鐘)
2.3.1 創建Clock類
2.3.2 創建修復Clock的模塊(module)
2.3.3 部署模塊
2.3.4 啟動sandbox
2.3.5 執行修復
一、安裝
jvm-sandbox的安裝非常簡單,簡言之就是執行下載文件夾里的install-local.sh,下載地址請訪問這里。
1. 下載
下載安裝包的zip文件解壓后目錄結構如下:
這里面bin目錄下的sandbox.sh就是jvm-sandbox交互的命令行腳本,但是沒有安裝前不能直接使用,因為還有一些變量沒有定義。example、module、provider目錄下的各種jar文件就是各種用途的module文件(什么是module以后再做介紹,這里理解成一個個插件即可)。cfg目錄下是sandbox的配置文件,lib目錄下的jar文件是sandbox的核心運行邏輯,以后再詳述。install-local.sh就是我們需要執行的安裝文件咯,保障它有執行權限并運行。
2. 運行安裝腳本
install-local.sh腳本運行的參數很簡單:
- -h : 幫助選項,打印命令幫助信息。
- -p : 指定本地安裝目錄。如果不指定,則默認安裝在"${HOME}/.opt"下,也就是當前用戶主目錄下的 ~/.opt/ 下面。
腳本執行完成后,輸出如下信息表明執行成功。
至此,sandbox安裝完畢,可以開始你的各種花式玩耍來練手了。SO EASY!!!
3. 閱讀安裝腳本
打開解壓安裝包里的install-local.sh,可以發現安裝邏輯還是簡單的。我們這里只介紹主要邏輯:
3.1 首先是定義安裝目錄變量
定義安裝目錄變量并且賦默認值,也就是前面說的如果不指定“-p“參數的默認安裝目錄。
typeset SANDBOX_INSTALL_PREFIXtypeset DEFAULT_SANDBOX_INSTALL_PREFIX="${HOME}/.opt"?
3.2 解析參數
跳過輔助方法exit_on_err()和usage() ,我們直接看main()。這個直接用系統方法getopts解析參數,-h則打印幫助信息并退出,-p則指定默認值。
while getopts "hp:" ARGdocase ${ARG} inh)usageexit;;p)SANDBOX_INSTALL_PREFIX=${OPTARG};;esacdone# if not appoint the install local, default is ${HOME}/.optif [[ -z ${SANDBOX_INSTALL_PREFIX} ]]; thenSANDBOX_INSTALL_PREFIX=${DEFAULT_SANDBOX_INSTALL_PREFIX}fi3.3 拷貝文件
接下來根據指定的(或者默認的)目錄地址創建文件夾,并將lib、module、provider下的文件拷貝到安裝目錄下。
# create install dirmkdir -p ${SANDBOX_INSTALL_LOCAL} \|| exit_on_err 1 "permission denied, create ${SANDBOX_INSTALL_LOCAL} failed."# copy filecp -r ./cfg ${SANDBOX_INSTALL_LOCAL}/ \&& cp -r ./lib ${SANDBOX_INSTALL_LOCAL}/ \&& cp -r ./module ${SANDBOX_INSTALL_LOCAL}/ \&& cp -r ./provider ${SANDBOX_INSTALL_LOCAL}/ \&& mkdir -p ${SANDBOX_INSTALL_LOCAL}/bin \|| exit_on_err 1 "permission denied, copy file failed."3.4 更新sandbox.sh腳本
?
?
# replace sandbox.sh\`s ${SANDBOX_HOME_DIR}cat ./bin/sandbox.sh \| sed "s:typeset SANDBOX_HOME_DIR:typeset SANDBOX_HOME_DIR=${SANDBOX_INSTALL_LOCAL}:g" \> ${SANDBOX_INSTALL_LOCAL}/bin/sandbox.sh \&& chmod +x ${SANDBOX_INSTALL_LOCAL}/bin/sandbox.sh \|| exit_on_err 1 "permission denied, replace ${SANDBOX_INSTALL_LOCAL}/bin/sandbox.sh failed."3.5 輸出結果
最后保存sandbox版本信息,并打印輸出sandbox相關信息:VERSION、PATH和安裝sandbox成功的信息,也就是前面說的判斷成功的信息。
# got sandbox's version local SANDBOX_VERSION=$(cat ${SANDBOX_INSTALL_LOCAL}/cfg/version)echo "VERSION=${SANDBOX_VERSION}" echo "PATH=${SANDBOX_INSTALL_LOCAL}" echo "install sandbox successful."二、快速上手
安裝好sandbox后就可以使用了,使用方法也很簡單,有兩種執行方式:ATTACH和AGENT兩種方式。前者將sandbox attach到指定java進程上;后者在啟動java虛擬機的時候直接指定參數。熟悉java agent的朋友應該知道agent的agentmain和premain兩種方式,這里咱們暫時留個伏筆。就筆者個人來說主要接觸ATTACH方式,在java程序啟動后動態加載并改變程序行為。
2.1 ATTACH方式啟動
不用重啟目標程序,直接attach上去執行操作。通過ps命令獲得目標程序的進程ID(例子中假設為47625),則執行命令:
# 假設目標JVM進程號為'47625' ./sandbox.sh -p 47625結果輸出如下信息表明啟動成功:
2.2 AGENT方式啟動
如果ATTACH方式啟動會影響性能,因為畢竟加載sandbox需要額外的開銷,則可以在程序啟動時添加JVM參數啟動:
-javaagent:/Path_to_Install_Directory/sandbox/lib/sandbox-agent.jar/Path_to_Install_Directory是sandbox的安裝目錄。
2.3 實操(wiki例子:修復一個損壞了的鐘)
官方文檔:https://github.com/alibaba/jvm-sandbox/wiki/FIRST-MODULE
2.3.1 創建Clock類
作為實驗對象的Clock類會循環拋出IllegalStateException異常,實驗目的就是通過一個用戶自定義的module對該運行中的Clock進行熱修復,使得程序不再拋出IllegalStateException。官方例子代碼如下:
package com.taobao.demo;/*** 報時的鐘*/ public class Clock {// 日期格式化private final java.text.SimpleDateFormat clockDateFormat= new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");/*** 狀態檢查*/final void checkState() {throw new IllegalStateException("STATE ERROR!");}/*** 獲取當前時間** @return 當前時間*/final java.util.Date now() {return new java.util.Date();}/*** 報告時間** @return 報告時間*/final String report() {checkState();return clockDateFormat.format(now());}/*** 循環播報時間*/final void loopReport() throws InterruptedException {while (true) {try {System.out.println(report());} catch (Throwable cause) {cause.printStackTrace();}Thread.sleep(1000);}}public static void main(String... args) throws InterruptedException {new Clock().loopReport();}}編譯運行后程序滾動拋出如下異常:
java.lang.IllegalStateException: STATE ERROR!at com.taobao.demo.Clock.checkState(Clock.java:16)at com.taobao.demo.Clock.report(Clock.java:34)at com.taobao.demo.Clock.loopReport(Clock.java:44)at com.taobao.demo.Clock.main(Clock.java:53) java.lang.IllegalStateException: STATE ERROR!at com.taobao.demo.Clock.checkState(Clock.java:16)at com.taobao.demo.Clock.report(Clock.java:34)at com.taobao.demo.Clock.loopReport(Clock.java:44)at com.taobao.demo.Clock.main(Clock.java:53) java.lang.IllegalStateException: STATE ERROR!at com.taobao.demo.Clock.checkState(Clock.java:16)at com.taobao.demo.Clock.report(Clock.java:34)at com.taobao.demo.Clock.loopReport(Clock.java:44)at com.taobao.demo.Clock.main(Clock.java:53)2.3.2 創建修復Clock的模塊(module)
創建一個Java工程clock-tinker,并且將parent指向sandbox-module-starter:
<parent><groupId>com.alibaba.jvm.sandbox</groupId><artifactId>sandbox-module-starter</artifactId><version>1.2.2</version></parent>筆者由于在github上下載了sandbox的源代碼,所以直接在以sandbox-module-starter為parent的工程sandbox-mgr-module中創建類BrokenClockTinkerModule:
package com.alibaba.jvm.sandbox.demo;import com.alibaba.jvm.sandbox.api.Information; import com.alibaba.jvm.sandbox.api.Module; import com.alibaba.jvm.sandbox.api.ProcessController; 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;@MetaInfServices(Module.class) @Information(id = "broken-clock-tinker") public class BrokenClockTinkerModule implements Module {@Resourceprivate ModuleEventWatcher moduleEventWatcher;@Command("repairCheckState")public void repairCheckState() {new EventWatchBuilder(moduleEventWatcher).onClass("com.taobao.demo.Clock").onBehavior("checkState").onWatch(new AdviceListener() {/*** 攔截{@code com.taobao.demo.Clock#checkState()}方法,當這個方法拋出異常時將會被* AdviceListener#afterThrowing()所攔截*/@Overrideprotected void afterThrowing(Advice advice) throws Throwable {// 在此,你可以通過ProcessController來改變原有方法的執行流程// 這里的代碼意義是:改變原方法拋出異常的行為,變更為立即返回;void返回值用null表示ProcessController.returnImmediately(null);}});}}代碼中的注解和代碼含義大家先不用在意,能猜透哪些算哪些,接下來我們要把module代碼部署成可用的module。
2.3.3 部署模塊
部署模塊就是把剛才編寫的BrokenClockTinkerModule類打包成jar包,然后復制該jar包到sandbox安裝目錄下module/下。首先在工程pom文件所在目錄執行命令:
mvn clean package然后將打包的sandbox-mgr-module.jar(筆者是直接在sandbox-mgr-module工程中添加的BrokenClockTinkerModule,具體jar包名稱根據pom配置定)文件復制到sandbox安裝目錄下的module目錄下。
cp target/sandbox-mgr-module.jar /Path_to_Install_Dir/sandbox/module/sandbox-mgr-module.jar2.3.4 啟動sandbox
現在有了實驗對象Clock,也部署了module,接下來啟動sandbox(2.1部分ATTACH方式啟動)。
./sandbox.sh -p 47625當看到2.1部分所述的信息時表示掛載成功,可以進一步通過命令驗證編寫的模塊正常加載:
./sandbox.sh -p 47625 -l2.3.5 執行修復
module啟動后,執行下面命令調用模塊方法進行修復:
./sandbox.sh -p 47625 -d 'broken-clock-tinker/repairCheckState'該命令執行broken-clock-tinker模塊下repairCheckState方法,執行后之前的Clock程序輸出變為:
java.lang.IllegalStateException: STATE ERROR!at com.example.demo.jvmsandbox.Clock.checkState(Clock.java:12)at com.example.demo.jvmsandbox.Clock.report(Clock.java:30)at com.example.demo.jvmsandbox.Clock.loopReport(Clock.java:40)at com.example.demo.jvmsandbox.Clock.main(Clock.java:49) java.lang.IllegalStateException: STATE ERROR!at com.example.demo.jvmsandbox.Clock.checkState(Clock.java:12)at com.example.demo.jvmsandbox.Clock.report(Clock.java:30)at com.example.demo.jvmsandbox.Clock.loopReport(Clock.java:40)at com.example.demo.jvmsandbox.Clock.main(Clock.java:49) 2019-12-17 18:37:07 2019-12-17 18:37:08 2019-12-17 18:37:09 2019-12-17 18:37:10 2019-12-17 18:37:11這口破損的鐘被修復了!!!
總結
以上是生活随笔為你收集整理的AOP—JVM SandBox—快速上手的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AOP—JVM SandBox—底层原理
- 下一篇: 分布式服务常见问题—访问量统计如何做?