澳门威斯尼斯人手机版:七年打磨,深刻剖判C

原标题:去何地系统高可用之法:搭建故障练习平台

澳门威斯尼斯人手机版 1

Classloader担任将Class加载到JVM中,况兼规定由非常ClassLoader来加载(父优先的品级加运载飞机制)。还恐怕有叁个任务正是将Class字节码重新解释为JVM统一供给的格式

在面向对象编制程序实施中,大家经过重重的类来协会贰个头眼昏花的类别,这一个类之间相互关系、调用使她们的关系形成了三个叶影参差紧凑的网络。当系统运维时,出于品质、财富利用多地点的虚拟,大家不恐怕要求JVM 一遍性将总体的类都加载成功,而是只加载能够帮忙系统顺遂运行和周转的类和财富就可以。那么在系统运行进程中只要必要动用未在运转时加载的类或财富时该如何做呢?那就要靠类加载器来产生了。

事关知识点:APM, java Agent, plugin, bytecode, asm, InvocationHandler, smail

小编介绍

阿里妹导读:调整和减弱故障的最佳格局就是让故障日常性的发生。通过不停重复失利进度,持续进步系统的容错和弹性工夫。今日,阿里Baba(Alibaba)把七年来在故障演习领域的新意和进行汇浓缩而成的工具实行开源,它就是“ChaosBlade”。要是您想要提高开荒功能,不妨来打探一下。

1.Classloader类结构分析

哪些是类加载器

类加载器(ClassLoader)正是在系统运维进程中动态的将字节码文件加载到 JVM 中的工具,基于这些工具的全方位类加载流程,大家称作类加运载飞机制。大家在 IDE 中编辑的都以源代码文件,未来缀名 .java 的文本方式存在于磁盘上,通过编译后生成后缀名 .class 的字节码文件,ClassLoader 加载的正是那些字节码文件。

APM : 应用程序质量管理。 2013年时海外的APM行当 NewRelic 和 应用程式Dynamics 已经在该领域拔得头筹,本国近些年来也应际而生有的APM厂家,如: 听云, OneAPM, 博睿 云智慧,Ali百川码力。 (据剖判,国内android端方案都以抄袭NewRelic集团的,由于该商场的sdk未混淆,产业界良心)

王鹏,二零一七年加入去何地机票工作部,首要从事后端研究开发工作,最近在机票工作部担任行程单和故障演习平台以及公共服务ES、数据同步中间件等皮之不存毛将焉附的研究开发专门的职业。

高可用架构是维系服务牢固性的为主。

(1)首要由七个形式,分别是defineClass,findClass,loadClass,resolveClass
  • <1>defineClass(byte[] , int ,int) 将byte字节流剖析为JVM能够分辨的Class对象(直接调用那一个点子生成的Class对象还并未有resolve,那一个resolve将会在那一个目的真正实例化时resolve)

  • <2>findClass,通过类名去加载对应的Class对象。当大家兑现自定义的classLoader平日是重写那几个办法,根据传入的类名找到对应字节码的文件,并经过调用defineClass剖判出Class独享

  • <3>loadClass运营时能够经过调用此办法加载多少个类(由于类是动态加载进jvm,用有些加载多少的?)

  • <4>resolveClass手动调用这一个使得被加到JVM的类被链接(剖判resolve那一个类?)

有怎么着类加载器

Java 默许提供了八个 ClassLoader,分别是 AppClassLoader、ExtClassLoader、BootStrapClassLoader,依次后面一个分别是前面二个的「父加载器」。父加载器不是「父类」,三者之间未有继续关系,只是因为类加载的流水生产线使三者之间产生了老爹和儿子关系,下文仲详细汇报。

能做怎么样: crash监察和控制,卡顿监察和控制,内存监察和控制,扩张trace,网络质量监察和控制,app页面自动埋点,等。

去哪个地方网二零零七年树立现今,随着系统规模的逐月扩大,已经有无数个使用连串,这几个系统里头的耦合度和链路的复杂度不断抓好,对于大家创设遍及式高可用的体系框架结构具备巨大挑衅。我们必要一个平台在运维期自动注入故障,查证故障预案是还是不是起效——故障练习平台。

Alibaba在海量互连网服务以及每年双11场景的进行进程中,沉淀出了席卷全链路压测、线上流量管理调节、故障演习等高可用宗旨手艺,并透过开源和云上服务的款式对外输出,以支援集团客户和开垦者享受阿里Baba(Alibaba)的技艺红利,升高开拓效用,减少职业的塑造流程。

(2)完成自定义ClassLoader一般会持续UEscortLClassLoader类,因为那么些类达成了超越八分之四格局。

BootStrapClassLoader

BootStrapClassLoader 也叫「根加载器」,它是脱离 Java 语言,使用 C/C 编写的类加载器,所以当您尝试使用 ExtClassLoader 的实例调用 getParent() 方法获得其父加载器时会获得两个 null 值。

// 返回一个 AppClassLoader 的实例ClassLoader appClassLoader = this.getClass().getClassLoader();// 返回一个 ExtClassLoader 的实例ClassLoader extClassLoader = appClassLoader.getParent();// 返回 null,因为 BootStrapClassLoader 是 C/C   编写的,无法在 Java 中获得其实例ClassLoader bootstrapClassLoader = extClassLoader.getParent();

根加载器会私下认可加载系统变量 sun.boot.class.path 钦点的类库(jar 文件和 .class 文件),暗中同意是 $JRE_HOME/lib 下的类库,如 rt.jar、resources.jar 等,具体能够出口该意况变量的值来查阅。

String bootClassPath = System.getProperty("sun.boot.class.path");String[] paths = bootClassPath.split(":");for (String path : paths) {    System.out.println;}// output// /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/resources.jar// /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/rt.jar// /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/sunrsasign.jar// /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/jsse.jar// /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/jce.jar// /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/charsets.jar// /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/jfr.jar// /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/classes

除此而外加载这么些暗许的类库外,也足以运用 JVM 参数 -Xbootclasspath/a 来追加额外部供给要让根加载器加载的类库。比方大家自定义贰个 com.ganpengyu.boot.DateUtils 类来让根加载器加载。

package com.ganpengyu.boot;import java.text.SimpleDateFormat;import java.util.Date;public class DateUtils {    public static void printNow() {        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        System.out.println(sdf.format(new Date;    }}

我们将其塑产生四个名称叫 gpy-boot 的 jar 包放到 /Users/yu/Desktop/lib 下,然后写二个测量检验类去尝尝加载 DateUtils。

public class Test {    public static void main(String[] args) throws Exception {        Class<?> clz = Class.forName("com.ganpengyu.boot.DateUtils");        ClassLoader loader = clz.getClassLoader();        System.out.println(loader == null);    }}

运维这几个测量试验类:

java -Xbootclasspath/a:/Users/yu/Desktop/lib/gpy-boot.jar -cp /Users/yu/Desktop/lib/gpy-boot.jar:. Test

能够见到输出为 true,也正是说加载 com.ganpengyu.boot.DateUtils 的类加载器在 Java 中无法获得其援用,而任何类都无法不经过类加载器加载技术被选择,所以测算出那些类是被 BootStrapClassLoader 加载的,也验证了 -Xbootclasspath/a 参数确实能够追加须求被根加载器额外加载的类库。

一句话来讲,对于 BootStrapClassLoader 那几个根加载器大家必要精晓三点:

  1. 根加载器使用 C/C 编写,大家不能在 Java 中拿走其实例
  2. 根加载器暗许加载系统变量 sun.boot.class.path 钦赐的类库
  3. 能够接纳 -Xbootclasspath/a 参数追加根加载器的暗许加载类库

属性监察和控制其实就是hook 代码到项目代码中,进而成就各样监督。常规手腕都以在品种中扩展代码,但什么实现非侵入式的,即二个sdk就可以。

一、背景

举例说,借助Ali云品质测量试验 PTS,高功能营造全链路压测类别,通过开源组件 Sentinel 完结限流和贬低功用。那贰遍,经历了 6 年时刻的精雕细刻和实施,累计在线上实践练习场景达数万次,我们将Alibaba在故障练习领域的创新意识和实施,浓缩成一个混沌工程工具,并将其开源,命名称为ChaosBlade。

2.ClassLoader的阶段加载机制

ExtClassLoader

ExtClassLoader 也叫「扩充类加载器」,它是贰个运用 Java 完成的类加载器(sun.misc.Launcher.ExtClassLoader),用于加载系统所急需的增添类库。默许加载系统变量 java.ext.dirs 钦命地方下的类库,平日是 $JRE_HOME/lib/ext 目录下的类库。

public static void main(String[] args) {    String extClassPath = System.getProperty("java.ext.dirs");    String[] paths = extClassPath.split(":");    for (String path : paths) {        System.out.println;    }}// output// /Users/leon/Library/Java/Extensions// /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/ext// /Library/Java/Extensions// /Network/Library/Java/Extensions// /System/Library/Java/Extensions// /usr/lib/java

咱们得以在运营时修改java.ext.dirs 变量的值来修改增加类加载器的默许类库加载目录,但经常并不提出如此做。假如大家的确有需求扩充类加载器在运转时加载的类库,可以将其放置在暗许的加载目录下。同理可得,对于 ExtClassLoader 那些扩张类加载器大家须要掌握两点:

  1. 扩展类加载器是行使 Java 完结的类加载器,我们能够在先后中获得它的实例并行使。
  2. 一般说来不提议修改java.ext.dirs 参数的值来修改暗许加载目录,如有必要,能够就要加载的类库放到那些暗中认可目录下。

1. 如何hook

断面编制程序-- AOP。大家的方案是AOP的一种,通过修改app class字节码的情势将大家项目标class文件举行退换,进而成就放权我们的监察代码。

澳门威斯尼斯人手机版 2androidbuilder.jpg

通过翻看Adnroid编写翻译流程图,能够领略编译器会将具有class文件打包称dex文件,最后打包成apk。那么我们就必要在class编写翻译成dex文件的时候实行代码注入。比如本人想总结某些方法的推行时间,那本身只须要在每一个调用了这些办法的代码前后都加一个岁月总括就可以了。关键点就在于编写翻译dex文件时候注入代码,那些编译进程是由dx实行,具体类和方法为com.android.dx.command.dexer.Main#processClass。此办法的第贰个参数正是class的byte数组,于是我们只需求在走入processClass方法的时候用ASM工具对class进行改建并替换掉首个参数,最终生成的apk就是大家退换之后的了。

类:com.android.dx.command.dexer.Main

新的难关: 要让jvm在实践processClass以前先进行大家的代码,必要求对com.android.dx.command.dexer.Main(以下简称为dexer.Main)实行改建。怎么着能力完毕那些指标?那时Instrumentation和VirtualMachine就上场了,参照他事他说加以考察第二节。

那是某职业部的种类拓扑图:

ChaosBlade 是什么?

ChaosBlade 是一款遵守混沌工程实践原理,提供丰富故障场景达成,帮忙遍布式系统提高容错性和可复苏性的愚拙工程工具,可完毕底层故障的注入,特点是操作轻易、无侵入、扩大性强。

ChaosBlade 基于 Apache License v2.0 开源合同,最近有 chaosblade 和 chaosblade-exe-jvm 三个客栈。

chaosblade 饱含 CLI 和应用 Golang 落成的基本功能源、容器相关的愚钝实验施行推行模块。chaosblade-exe-jvm 是对运作在 JVM 上的运用施行混沌实验的施行器。

ChaosBlade 社区三番五次还有只怕会增多 C 、Node.js 等其他语言的无知实验试行器。

澳门威斯尼斯人手机版 3

(1)JVM平台提供三层的ClassLoader,那三层ClassLoader能够分成两类,分别是服务JVM自个儿的,和劳务广大普通类的。分别是:
  • <1>BootstrapClassLoader:重要加载JVM自己专门的学问所须要的类,该ClassLoader未有父类加载器和子类加载器

  • <2>ExtClassLoader:这几个类加载器同样是JVM自己的一有些,然并非由JVM达成,重要用来加载System.getProperty(“java.ext.dirs”)目录地下的类,如本机的值“D:javajdk7jrelibext;C:WindowsSunJavalibext”

  • <3>AppClassLoader:加载System.getProperty("java.class.path")(注意了在ide中运作程序时,该值日常是该项目标classes文件夹)中的类。全体的自定义类加载器不管直接促成ClassLoader,是连续自UMuranoLClassLoader或其子类,其父加载器(注意:父加载器与父类的个别)都是AppClassLoader,因为不论是调用哪个父类的构造器,最终都将调用getSystemClassLoader作为父加载器,而该模式再次来到的难为AppClassLoader。(当应用程序中绝非其他自定义的classLoader,那么除了System.getProperty(“java.ext.dirs”)目录中的类,其余类都由AppClassLoader加载)

AppClassLoader

AppClassLoader 也叫「应用类加载器」,它和 ExtClassLoader 同样,也是选取Java 完毕的类加载器(sun.misc.Launcher.AppClassLoader)。它的职能是加载应用程序 classpath 下全部的类库。那是我们最常打交道的类加载器,大家在先后中调用的好些个 getClassLoader() 方法重回的都以它的实例。在大家自定义类加载器时一旦未有特地钦命,那么大家自定义的类加载器的暗中认可父加载器也是以此应用类加载器。可想而知,对于 AppClassLoader 那么些应用类加载器大家须要驾驭两点:

  1. 应用类加载器是选取 Java 达成的类加载器,肩负加载应用程序 classpath 下的类库。
  2. 应用类加载器是和大家最常打交道的类加载器。
  3. 不曾专门内定的情景下,自定义类加载器的父加载器正是应用类加载器。

2. hook 到哪里

一期首固然网络品质监察和控制。怎么着能收获到互联网数据经过调查研商发现日前有上边集中方案:

  • root手提式有线话机,通过adb 命令实行收缴。
  • 树立vpn,将兼具网络诉求举行收缴。
  • 参谋听云,newrelic等出品,针对一定库开展代理截获。

或许还应该有其它的方法,需求后续实验研讨。

脚下我们参考newrelic等厂家产品,针对一定互连网乞求库开展代理的的不二等秘书技张开互连网数据截获。比方okhtt3, httpclient, 等互连网库。

In general, a javaagent is a JVM “plugin”, a specially crafted .jar file, that utilizes the Instrumentation API that the JVM provides.

由于大家要修改Dexer 的Main类, 而该类是在编写翻译时期由java虚构机运行的, 所以我们要求经过agent来修改dexer Main类。

javaagent的重要功用如下:

  • 能够在加载class文件此前作拦截,对字节码做修改
  • 能够在运作期对已加载类的字节码做改造

JVMTI:JVM Tool Interface,是JVM暴表露来的片段供客商扩大的接口集结。JVMTI是依附事件驱动的,JVM每试行到早晚的逻辑就能够调用一些事变的回调接口,那么些接口能够供开拓者增加自身的逻辑。

instrument agent: javaagent功用正是它来贯彻的,其他instrument agent还恐怕有些名称叫JPLISAgent(Java Programming Language Instrumentation Services Agent),这些名字也统统反映了其最本质的遵守:就是极其为Java语言编写的插桩服务提供支撑的。

二种加载agent的措施:

  • 在运转时加载, 运维JVM时钦赐agent类。这种办法,Instrumentation的实例通过agent class的premain方法被传出。
  • 在运作时加载,JVM提供一种当JVM运维成功后开启agent机制。这种情状下,Instrumention实例通过agent代码中的的agentmain传入。

参谋例子instrumentation 功效介绍(javaagent)

有了javaagent, 大家就可以在编写翻译app时再一次修改dex 的Main类,对应修改processClass方法。

哪些修改class文件? 大家须要精通java字节码,然后要求精通ASM开辟。通过ASM编程来修改字节码,进而修改class文件。(也足以应用javaassist来扩充修改)

在介绍字节代码指令此前,有必不可缺先来介绍 Java 设想机推行模型。大家知晓,Java 代码是 在线程内部实践的。每一种线程都有谈得来的施行栈,栈由帧组成。每一个帧表示一个方法调用:每一次调用一个主意时,会将贰个新帧压入当前线程的实施栈。当方法重返时,或许是健康重回,也许是因为非常再次回到,会将以此帧从实施栈中弹出,实施进程在发出调用的艺术中继续举办(那些方 法的帧现在投身栈的上方)。

每一帧包含两部分:叁个局地变量部分和四个操作数栈部分。局地变量部分含有可依据索引 以随机顺序访谈的变量。由名字能够看来,操作数栈部分是三个栈,个中包蕴了供字节代码指令 用作操作数的值。

字节代码指令 字节代码指令由一个标记该指令的操作码和牢固数指标参数组成:

  • 操作码是一个无符号字节值——即字节代码名
  • 参数是静态值,分明了准确的吩咐行为。它们紧跟在操作码之后给出.比方GOTO标志指令(其操作码的值为 167)以贰个指明下一条待试行命令的号子作为参数标识。不要 将指令参数与指令操作数相混淆:参数值是静态已知的,存储在编写翻译后的代码中,而 操作数值来自操作数栈,唯有到运维时技能清楚。

参考:

遍布指令:

  • const 将怎么样数据类型压入操作数栈。
  • push 表示将单字节或短整型的常量压入操作数栈。
  • ldc 代表将如何品种的数目从常量池中压入操作数栈。
  • load 将某项目标部分变量数据压入操作数栈顶。
  • store 将操作数栈顶的多寡存入钦点的一些变量中。
  • pop 从操作数栈顶弹出多少
  • dup 复制栈顶的数额并将复制的值也压入栈顶。
  • swap 沟通栈顶的数量
  • invokeVirtual 调用实例方法
  • invokeSepcial 调用超类构造方法,实例先导化,私有方法等。
  • invokeStatic 调用静态方法
  • invokeInterface 调用接口
  • getStatic
  • getField
  • putStatic
  • putField
  • New

查看demo:Java源代码

public static void print(String param) { System.out.println("hello "   param); new TestMain().sayHello();}public void sayHello() { System.out.println("hello agent");}

字节码

// access flags 0x9 public static print(Ljava/lang/String;)V GETSTATIC java/lang/System.out : Ljava/io/PrintStream; NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V LDC "hello " INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 0 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V NEW com/paic/agent/test/TestMain DUP INVOKESPECIAL com/paic/agent/test/TestMain.<init> ()V INVOKEVIRTUAL com/paic/agent/test/TestMain.sayHello ()V RETURN public sayHello()V GETSTATIC java/lang/System.out : Ljava/io/PrintStream; LDC "hello agent" INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V RETURN

出于程序解析、生成和转移技能的用处众多,所以人们针对广大言语落成了成都百货上千用于解析、 生成和转变程序的工具,那几个语言中就总结 Java 在内。ASM 便是为 Java 语言设计的工具之一, 用于开展运行时类生成与调换。于是,大家设计了 ASM1库,用于拍卖经过编写翻译 的 Java 类。

ASM 并非惟一可生成和转变已编写翻译 Java 类的工具,但它是最新、最高效的工具之一,可 从 下载。其首要优点如下:

  • 有二个简短的模块API,设计宏观、使用方便。
  • 文书档案齐全,具备三个连锁的Eclipse插件。
  • 帮助新型的 Java 版本——Java 7。
  • 小而快、特别可信。
  • 负有庞大的客户社区,可感到新顾客ﰁ供协助。
  • 源许可开放,差十分少允许专擅使用。

澳门威斯尼斯人手机版 4ASM_transfer.png

核心类: ClassReader, ClassWriter, ClassVisitor

参考demo:

{ // print 方法的ASM代码 mv = cw.visitMethod(ACC_PUBLIC   ACC_STATIC, "print", "(Ljava/lang/String;)V", null, null); mv.visitCode(); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); mv.visitInsn; mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false); mv.visitLdcInsn; mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); mv.visitVarInsn; mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); mv.visitTypeInsn(NEW, "com/paic/agent/test/TestMain"); mv.visitInsn; mv.visitMethodInsn(INVOKESPECIAL, "com/paic/agent/test/TestMain", "<init>", "()V", false); mv.visitMethodInsn(INVOKEVIRTUAL, "com/paic/agent/test/TestMain", "sayHello", "()V", false); mv.visitInsn; mv.visitEnd();}{ //sayHello 的ASM代码 mv = cw.visitMethod(ACC_PUBLIC, "sayHello", "()V", null, null); mv.visitCode(); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitLdcInsn("hello agent"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); mv.visitInsn; mv.visitEnd();}

VirtualMachine有个loadAgent方法,它钦命的agent会在main方法前运转,并调用agent的agentMain方法,agentMain的第2个参数是Instrumentation,那样我们就可见给Instrumentation设置ClassFileTransformer来落成对dexer.Main的改建,同样也足以用ASM来促成。一般的话,APM工具包括多个部分,plugin、agent和切实的事业jar包。这些agent就是大家说的由VirtualMachine运维的代理。而plugin要做的工作就是调用loadAgent方法。对于Android Studio来说,plugin就是多少个Gradle插件。 达成gradle插件能够用intellij创立三个gradle工程并落实Plugin< Project >接口,然后把tools.jar(在jdk的lib目录下)和agent.jar参与到Libraries中。在META-INF/gradle-plugins目录下开创多个properties文件,并在文书中投入一行内容“implementation-class=插件类的全限定名“。artifacs配置把源码和META-INF加上,但无法加tools.jar和agent.jar。(tools.jar 在 jdk中, 可是一般供给和谐拷贝到工程目录中的, agent.jar开垦到位后放到plugin工程中用来获取jar包路径)。

agent的贯彻相对plugin则复杂相当多,首先要求提供agentmain(String args, Instrumentation inst)方法,并给Instrumentation设置ClassFileTransformer,然后在transformer里改动dexer.Main。当jvm成功推行到大家设置的transformer时,就能够发觉传进来的class根本就没有dexer.Main。坑爹呢那是。。。前边提到了,试行dexer.Main的是dx.bat,相当于说,它和plugin根本不在三个经过里。

dx.bat其实是由ProcessBuilder的start方法运维的,ProcessBuilder有三个command成员,保存的是开发银行指标经过指导的参数,只要大家给dx.bat带上-javaagent参数就会给dx.bat所在经过内定大家的agent了。于是大家得以在施行start方法前,调用command方法获得command,并往当中插入-javaagent参数。参数的值是agent.jar所在的路子,可以采纳agent.jar个中三个class类实例的getProtectionDomain().getCodeSource().getLocation.getPath()得到。然而到了这里我们的主次只怕依然不可能准确改变class。假若大家把退换类的代码单独置于五个类中,然后用ASM生成字节码调用那几个类的方法来对command参数实行改造,就能发觉抛出了ClassDefNotFoundError错误。这里提到到了ClassLoader的知识。

有关ClassLoader的牵线很多,这里不再赘言。ProcessBuilder类是由Bootstrap ClassLoader加载的,而我辈自定义的类则是由AppClassLoader加载的。Bootstrap ClassLoader处于AppClassLoader的上层,大家了然,上层类加载器所加载的类是心有余而力不足直接援引下层类加载器所加载的类的。但假若下层类加载器加载的类实现或一而再了上层类加载器加载的类或接口,上层类加载器加载的类获取到下层类加载的类的实例就足以将其强制转型为父类,并调用父类的点子。那么些上层类加载器加载的接口,部分APM使用InvocationHandler。还或许有多少个难点,ProcessBuilder怎么技术收获到InvocationHandler子类的实例呢?有三个比较神奇的做法,在agent运维的时候,创设InvocationHandler实例,并把它赋值给Logger的treeLock成员。treeLock是一个Object对象,况且只是用来加锁的,未有其余用途。但treeLock是贰个final成员,所以记得要修改其修饰,去掉final。Logger同样也是由Bootstrap ClassLoader加载,那样ProcessBuilder就可以由此反射的办法来收获InvocationHandler实例了。(详见:宗旨代码例子)

上层类加载器所加载的类是力不可能及直接援用下层类加载器所加载的类的

层次 加载器
上层 BootStrapClassLoader ProcessBuilder
下层 AppClassLoader ProcessBuilderMethodVisitor操作的自定义类

这一句话的接头: 大家的目标是经过ProcessBuilderMethodVisitor将大家的代码写入ProcessBuilder.class中去让BootStrapClassLoader类加载器进行加载,而那时, BootStrapClassLoader是力所不比引用到我们自定义的类的,因为大家自定义的类是AppClassLoader加载的。

但假设下层类加载器加载的类达成或接续了上层类加载器加载的类或接口,上层类加载器加载的类获取到下层类加载的类的实例就可以将其挟持转型为父类,并调用父类的不二秘籍。

层次 加载器
上层 BootStrapClassLoader Looger
下层 AppClassLoader InvocationDispatcher

这句话的知晓: 这里我们可以阅览自定义类InvocationDispatcher是由AppClassLoader加载的, 大家在运作RewriterAgent(AppClassLoader加载)类时,通过反射的艺术将InvocationDispatcher对象放入Looger(由于援引了Looger.class,所以此时logger已经被BootStrapClassLoader加载)类的treelock对象中,即下层类加载器加载的类完结了上层类加载器加载的类;当大家透过ProcessBuilderMethodVisitor类管理ProcessBuilder.class文件时,能够由此Logger提取成员变量,插入对应的调用逻辑。当运营到ProcessBuilder时,再通过这段代码动态代理的法子调用对应的专业。能够将其挟持转型为父类,并调用父类的章程 ,请参考 这里详细介绍了invokeInterface 和 invokeVirtual 的分别。

贯彻上我们当前着重做那三种, 一种是代码调用替换, 另一种是代码包裹重回。主假若提前写好相应法规的轮换代码, 生成配置文件表, 在agent中visit每壹个class代码, 遇到对应相配调用时将实行代码替换。

ProcessBuilderMethodVisitor DexClassTransformer#createDexerMainClassAdapter InvocationDispatcher BytecodeBuilder

public BytecodeBuilder loadInvocationDispatcher() { this.adapter.visitLdcInsn(Type.getType(TransformConstant.INVOCATION_DISPATCHER_CLASS)); this.adapter.visitLdcInsn(TransformConstant.INVOCATION_DISPATCHER_FILED_NAME); this.adapter.invokeVirtual(Type.getType(Class.class), new Method("getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;")); this.adapter.dup(); this.adapter.visitInsn(Opcodes.ICONST_1); this.adapter.invokeVirtual(Type.getType(Field.class), new Method("setAccessible", "; this.adapter.visitInsn(Opcodes.ACONST_NULL); this.adapter.invokeVirtual(Type.getType(Field.class), new Method("get", "(Ljava/lang/Object;)Ljava/lang/Object;")); return this; }

解析

顺序 指令 描述
8 InvocationDispatcher object invokeVirtual 调用get方法返回具体实例对象
7 null ACONST_NULL null 入栈
6 Field object invokeVirtual 调用setAccessible,改为可访问的,目前栈中只剩一个对象
5 true ICONST_1 1 即为true,入栈
4 Field object dup 拷贝一份,目前栈中只剩两个对象
3 Field object invokeVirtual 调用getDeclaredField 获取treeLock存储的Field
2 treelock ldc treelock 入栈
1 Logger.class Type ldc Logger.class type 入栈

WrapMethodClassVisitor#MethodWrapMethodVisitor

private boolean tryReplaceCallSite(int opcode, String owner, String name, String desc, boolean itf) { Collection<ClassMethod> replacementMethods = this.context.getCallSiteReplacements(owner, name, desc); if (replacementMethods.isEmpty { return false; } ClassMethod method = new ClassMethod(owner, name, desc); Iterator<ClassMethod> it = replacementMethods.iterator(); if (it.hasNext { ClassMethod replacementMethod = it.next(); boolean isSuperCallInOverride = (opcode == Opcodes.INVOKESPECIAL) && !owner.equals(this.context.getClassName && this.name.equals && this.desc.equals; //override 方法 if (isSuperCallInOverride) { this.log.info(MessageFormat.format("[{0}] skipping call site replacement for super call in overriden method : {1}:{2}", this.context.getFriendlyClassName(), this.name, this.desc)); return false; } Method originMethod = new Method(name, desc); //处理init方法, 构造对象, 调用替换的静态方法来替换init。 if (opcode == Opcodes.INVOKESPECIAL && name.equals("<init>")) { //调用父类构造方法 if (this.context.getSuperClassName() != null && this.context.getSuperClassName().equals { this.log.info(MessageFormat.format("[{0}] skipping call site replacement for class extending {1}", this.context.getFriendlyClassName(), this.context.getFriendlySuperClassName; return false; } this.log.info(MessageFormat.format("[{0}] tracing constructor call to {1} - {2}", this.context.getFriendlyClassName(), method.toString); //开始处理创建对象的逻辑 //保存参数到本地 int[] arguments = new int[originMethod.getArgumentTypes().length]; for (int i = arguments.length -1 ; i >= 0; i--) { arguments[i] = this.newLocal(originMethod.getArgumentTypes; this.storeLocal(arguments[i]); } //由于init 之前会有一次dup,及创建一次, dup一次, 此时如果执行了new 和 dup 操作树栈中会有两个对象。 this.visitInsn(Opcodes.POP); if (this.newInstructionFound && this.dupInstructionFound) { this.visitInsn(Opcodes.POP); } //载入参数到操作数栈 for (int arg : arguments) { this.loadLocal; } //使用要替换的方法,执行静态方法进行对象创建 super.visitMethodInsn(Opcodes.INVOKESTATIC, replacementMethod.getClassName(), replacementMethod.getMethodName(), replacementMethod.getMethodDesc; //如果此时才调用了dup,也需要pop, (这一部分的场景暂时还没有构造出来, 上面的逻辑为通用的) if (this.newInstructionFound && !this.dupInstructionFound) { this.visitInsn(Opcodes.POP); } } else if (opcode == Opcodes.INVOKESTATIC) { //替换静态方法 this.log.info(MessageFormat.format("[{0}] replacing call to {1} with {2}", this.context.getFriendlyClassName(), method.toString(), replacementMethod.toString; super.visitMethodInsn(Opcodes.INVOKESTATIC, replacementMethod.getClassName(), replacementMethod.getMethodName(), replacementMethod.getMethodDesc; } else { // 其他方法调用, 使用新方法替换旧方法的调用。 先判断创建的对象是否为null, Method newMethod = new Method(replacementMethod.getMethodName(), replacementMethod.getMethodDesc; this.log.info(MessageFormat.format("[{0}] replacing call to {1} with {2}", this.context.getFriendlyClassName(), method.toString(), replacementMethod.toString; //从操作数栈上取原始参数类型到本地变量中 int[] originArgs = new int[originMethod.getArgumentTypes().length]; for (int i = originArgs.length -1 ; i >= 0; i--) { originArgs[i] = this.newLocal(originMethod.getArgumentTypes; this.storeLocal(originArgs[i]); } //操作数栈中只剩操作对象了, 需要dup, 拷贝一份作为检查新method的第一个参数。 this.dup(); //检查操作数栈顶对象类型是否和新method的第一个参数一致。 this.instanceOf(newMethod.getArgumentTypes; Label isInstanceOfLabel = new Label(); //instanceof 结果不等于0 则跳转到 isInstanceofLabel,执行替换调用 this.visitJumpInsn(Opcodes.IFNE, isInstanceOfLabel); //否则执行原始调用 for (int arg : originArgs) { this.loadLocal; } super.visitMethodInsn(opcode, owner, name, desc, itf); Label endLabel = new Label(); //跳转到结束label this.visitJumpInsn(Opcodes.GOTO, endLabel); this.visitLabel(isInstanceOfLabel); //处理替换的逻辑 //load 参数, 第一个为 obj, 后面的为原始参数 this.checkCast(newMethod.getArgumentTypes; for (int arg: originArgs) { this.loadLocal; } super.visitMethodInsn(Opcodes.INVOKESTATIC, replacementMethod.getClassName(), replacementMethod.getMethodName(), replacementMethod.getMethodDesc; //结束 this.visitLabel; } this.context.markModified(); return true; } return false; }

解析 详细见tryReplaceCallSite声明就可以。

澳门威斯尼斯人手机版 5

为啥要开源?

有的是商家现已上马关注并探究混沌工程,渐渐形成测量试验系统高可用,塑造对系统新闻不可缺点和失误的工具。但混沌工程领域这两天还处在贰个飞跃造成的等第,最佳施行和工具框架没有统一标准。实行混沌工程可能会推动一些暧昧的事情危害,经验和工具的枯槁也将更为阻止 DevOps 职员实行混沌工程。

混沌工程领域方今也是有过多精美的开源工具,分别覆盖某些圈子,但那几个工具的应用方法差距,个中有个别工具上手难度大,学习成本高,混沌实验工夫单一,使广大人对混沌工程领域停滞不前。

Alibaba公司在混沌工程领域曾经推行多年,将混沌实验工具 ChaosBlade 开源指标,我们愿意:

  • 让更两人询问并加入到混沌工程领域;
  • 缩水创设混沌工程的路线;
  • 並且借助社区的技能,完善越来越多的愚昧实验现象,共同推进混沌工程领域的前行。
(2)Jvm加载class文件到内具备三种方法,隐式加载和显示加载,常常这两种办法是名不副实使用的
  • <1>隐式加载:是经过JVM来自动加载必要的类到内部存储器的秘技,当有个别类被采用时,JVM开掘此类不在内部存款和储蓄器中,那么它就能够自动加载该类到内存

  • <2>彰显加载:通过调用this.getClasss.getClassLoader.loadClass(),Class.forName,自身达成的ClassLoader的findClass方法

自定义类加载器

除此而外上述三种 Java 默许提供的类加载器外,大家还是可以通过承袭 java.lang.ClassLoader 来自定义三个类加载器。若是在成立自定义类加载器时从没点名父加载器,那么默许使用 AppClassLoader 作为父加载器。关于自定义类加载器的开创和选择,大家会在后头的章节详细讲授。

8. 验证

将转变的apk反编写翻译,查看class 字节码。大家一般会经过JD-GUI来查看。我们来查看一下sample生成的结果:

private void testOkhttpCall() { OkHttpClient localOkHttpClient = new OkHttpClient.Builder; Object localObject = new Request.Builder().url("https://test3-fbtoam.pingan.com.cn:15443/btoa/portal/common/getPublicKey"); if (!(localObject instanceof Request.Builder)) { localObject = ((Request.Builder)localObject).build(); if ((localOkHttpClient instanceof OkHttpClient)) { break label75; } } label75: for (localObject = localOkHttpClient.newCalllocalObject);; localObject = OkHttp3Instrumentation.newCall((OkHttpClient)localOkHttpClient, localObject)) { localObject).enqueue(new Callback() { public void onFailure(Call paramAnonymousCall, IOException paramAnonymousIOException) { } public void onResponse(Call paramAnonymousCall, Response paramAnonymousResponse) throws IOException { } }); return; localObject = OkHttp3Instrumentation.build((Request.Builder)localObject); break; } }

下面的代码推断未有几人能够看懂, 尤其for循环里面包车型大巴逻辑。其实是由于分化的反编译工具形成的剖析难题造成的,所以看起来逻辑混乱,不能够符合预期。

想用查看真实的结果, 大家来看下反编写翻译后的smail。详细smail指令参照他事他说加以考察

.method private testOkhttpCall()V .locals 6 .prologue .line 35 const-string v3, "https://test3-fbtoam.pingan.com.cn:15443/btoa/portal/common/getPublicKey" .line 36 .local v3, "url":Ljava/lang/String; new-instance v4, Lokhttp3/OkHttpClient$Builder; invoke-direct {v4}, Lokhttp3/OkHttpClient$Builder;-><init>()V invoke-virtual {v4}, Lokhttp3/OkHttpClient$Builder;->build()Lokhttp3/OkHttpClient; move-result-object v1//new OkHttpClient.Builder; 即为okhttpclient,放到 v1 中 .line 37 .local v1, "okHttpClient":Lokhttp3/OkHttpClient; new-instance v4, Lokhttp3/Request$Builder; invoke-direct {v4}, Lokhttp3/Request$Builder;-><init>()V invoke-virtual {v4, v3}, Lokhttp3/Request$Builder;->url(Ljava/lang/String;)Lokhttp3/Request$Builder; move-result-object v4 //new Request.Builder().url执行了这一段语句,将结果放到了v4中。 instance-of v5, v4, Lokhttp3/Request$Builder; if-nez v5, :cond_0 invoke-virtual {v4}, Lokhttp3/Request$Builder;->build()Lokhttp3/Request; move-result-object v2 .line 38 .local v2, "request":Lokhttp3/Request; //判断v4中存储的是否为Request.Builder类型,如果是则跳转到cond_0, 否则执行Request.Builder.build()方法,将结果放到v2中. :goto_0 instance-of v4, v1, Lokhttp3/OkHttpClient; if-nez v4, :cond_1 invoke-virtual {v1, v2}, Lokhttp3/OkHttpClient;->newCall(Lokhttp3/Request;)Lokhttp3/Call; move-result-object v0 .line 39 .end local v1 # "okHttpClient":Lokhttp3/OkHttpClient; .local v0, "call":Lokhttp3/Call; //goto_0 标签:判断v1 中的值是否为 OKHttpclient 类型, 如果是跳转为cond_1 , 否则调用OKHttpclient.newCall, 并将结果放到v0 中。 :goto_1 new-instance v4, Lcom/paic/apm/sample/MainActivity$1; invoke-direct {v4, p0}, Lcom/paic/apm/sample/MainActivity$1;-><init>(Lcom/paic/apm/sample/MainActivity;)V invoke-interface {v0, v4}, Lokhttp3/Call;->enqueue(Lokhttp3/Callback;)V .line 51 return-void //goto_1 标签: 执行 v0.enqueue(new Callback;并return; .line 37 .end local v0 # "call":Lokhttp3/Call; .end local v2 # "request":Lokhttp3/Request; .restart local v1 # "okHttpClient":Lokhttp3/OkHttpClient; :cond_0 check-cast v4, Lokhttp3/Request$Builder; invoke-static {v4}, Lcom/paic/agent/android/instrumentation/okhttp3/OkHttp3Instrumentation;->build(Lokhttp3/Request$Builder;)Lokhttp3/Request; move-result-object v2 goto :goto_0 //cond_0:标签: 执行com.paic.agent.android.instrumentation.okhttp3.OkHttp3Instrumentation.build, 并将结果放到v2中,并goto 到 goto_0 .line 38 .restart local v2 # "request":Lokhttp3/Request; :cond_1 check-cast v1, Lokhttp3/OkHttpClient; .end local v1 # "okHttpClient":Lokhttp3/OkHttpClient; invoke-static {v1, v2}, Lcom/paic/agent/android/instrumentation/okhttp3/OkHttp3Instrumentation;->newCall(Lokhttp3/OkHttpClient;Lokhttp3/Request;)Lokhttp3/Call; move-result-object v0 goto :goto_1 //cond_1 标签: 执行com.paic.agent.android.instrumentation.okhttp3.OkHttp3Instrumentation.newCall, 并将结果放到v0中, goto 到goto_1 .end method

解析后的伪代码

String v3 = "https://test3-fbtoam.pingan.com.cn:15443/btoa/portal/common/getPublicKey";object v1 = new OkhttpClient.Builder;object v4 = new Reqeust.Builder;object v2 ;object v0 ;if (v4 instanceof Request.Builder) { cond_0: v2 = com.paic.agent.android.instrumentation.okhttp3.OkHttp3Instrumentation.build; } else { v2 = (Request.Builder)v4.build();}goto_0:if (v1 instanceof OkHttpClient) { cond_1: v0 = com.paic.agent.android.instrumentation.okhttp3.OkHttp3Instrumentation.newCall;} else { v0 = v1.newCall; // v0 is Call}goto_1:v4 = new Callback();v0.enqueue;return;

翻开伪代码, 符合预期结果。验证完结。

系统里面包车型地铁依赖性非常复杂、调用链路很深、服务期间没有分支。在这种复杂的信赖下,系统发生了几起故障:

ChaosBlade 能消除什么难题?

权衡微服务的容错技艺

由此模拟调用延迟、服务不可用、机器能源满载等,查看产生故障的节点或实例是不是被机关隔断、下线,流量调节是不是精确,预案是或不是行得通,同不时间观看系统完全的 QPS 或 RT 是或不是受影响。在此基础上得以减缓扩大故障节点范围,验证上游服务限流降级、熔断等是否行得通。最后故障节点扩充到供给服务超时,估摸系统容错红线,度量系统容错能力。

证实容器编排配置是不是站得住

经过模拟杀服务 Pod、杀节点、增大 Pod 财富负载,观看系统服务可用性,验证别本配置、能源限制配置以及 Pod 下安顿的器皿是还是不是站得住。

测验 PaaS 层是不是结实

透过模拟上层能源负载,验证调整类别的实用;模拟注重的遍及式存款和储蓄不可用,验证系统的容错技术;模拟调节节点不可用,测量检验调治职分是不是自动员搬迁移到可用节点;模拟主备节点故障,测量检验主备切换是或不是正规。

证实监察和控制告警的时效性

透过对系统注入故障,验香港证肆期货(Futures)交易监督委员会察和控制指标是还是不是可信赖,监察和控制维度是不是健全,告警阈值是或不是创制,告警是还是不是飞快,告警接收人是或不是科学,文告路子是不是可用等,进步监督告警的标准和时效性。

固化与化解难点的应急妙技

经过故障突袭,随机对系统注入故障,调查相关人口对难点的应急力量,以及难题反馈、管理流程是或不是创设,到达以战养战,磨练人稳固与缓和难题的本事。

(3)上级委托机制:当一个加载器加载类字时,先委托其父加载器加载,若加载成功则反映给该加载器,若父加载器不能加载,则由该加载器加载

类加载器的启航顺序

上文已经涉及过 BootStrapClassLoader 是一个利用 C/C 编写的类加载器,它曾经松开到了 JVM 的内核之中。当 JVM 运维时,BootStrapClassLoader 也会跟着运营并加载主题类库。当焦点类库加载成功后,BootStrapClassLoader 会创造 ExtClassLoader 和 AppClassLoader 的实例,五个 Java 完结的类加载器将会加载本身承受路线下的类库,那么些进程我们能够在 sun.misc.Launcher 中窥见。

  • 弱重视挂掉,主流程挂掉,修改报废凭证的开采情形,下单主流程退步;
  • 基本服务调用量陡增,某服务超时引起相关联的所有服务“雪崩”;
  • 机房互连网可能某个机器挂掉,无法提供基本服务。

成效和特征

气象丰盛度高

ChaosBlade 支持的无知实验现象不止覆盖基础资源,如 CPU 满载、磁盘 IO 高、网络延迟等,还满含运营在 JVM 上的运用试验现象,如 Dubbo 调用超时和调用万分、钦命方法延迟或抛十分以及再次回到特定值等,同期涉嫌容器相关的试验,如杀容器、杀 Pod。后续会没完没了的扩展实行现象。

行使轻易,易于精通

ChaosBlade 通过 CLI 情势推行,具备友好的下令提醒意义,能够回顾连忙的左侧使用。命令的书写遵从Alibaba集团内多年故障测量检验和演习施行抽象出的故障注入模型,档次明显,易于阅读和领悟,减弱了混沌工程施行的三昧。

气象扩展方便

不无的 ChaosBlade 实验推行器一样遵守上述提到的故障注入模型,使实验现象模型统一,便于开荒和护卫。模型本人老妪能解,学习花费低,可以依靠模型方便急速的庞大更加的多的无知实验现象。

澳门威斯尼斯人手机版 6

3.哪些加载class文件:

分为四个步骤 加载字节码到内部存款和储蓄器、Linking、类字节起初化赋值

ExtClassLoader 的开创进度

我们将 Launcher 类的构造方法源码精简展现如下:

public Launcher() {    // 创建 ExtClassLoader    Launcher.ExtClassLoader var1;    try {        var1 = Launcher.ExtClassLoader.getExtClassLoader();    } catch (IOException var10) {        throw new InternalError("Could not create extension class loader", var10);    }    // 创建 AppClassLoader    try {        this.loader = Launcher.AppClassLoader.getAppClassLoader;    } catch (IOException var9) {        throw new InternalError("Could not create application class loader", var9);    }    // 设置线程上下文类加载器    Thread.currentThread().setContextClassLoader(this.loader);    // 创建 SecurityManager}

能够看出当 Launcher 被初叶化时就能挨个成立 ExtClassLoader 和 AppClassLoader。我们进来 getExtClassLoader() 方法并追踪创制流程,发掘这里又调用了 ExtClassLoader 的构造方法,在这么些构造方法里调用了父类的构造方法,那正是 ExtClassLoader 创设的关键步骤,注意这里传出父类构造器的第三个参数为 null。接着大家去查看那几个父类构造方法,它身处 java.net.URLClassLoader 类中:

URLClassLoader(URL[] urls, ClassLoader parent,                          URLStreamHandlerFactory factory)

透过那几个构造方法的签字和注释我们能够明显的明白,第4个参数 parent 表示的是日前要创立的类加载器的父加载器。结合前边大家提到的 ExtClassLoader 的父加载器是 JVM 内核中 C/C 开荒的 BootStrapClassLoader,且不能在 Java 中拿走这么些类加载器的引用,同期每一个类加载器又一定有三个父加载器,大家能够反证出,ExtClassLoader 的父加载器正是 BootStrapClassLoader。

八个故障原因:

ChaosBlade 的演进史

EOS(2012-2015):故障练习平台的开始时代版本,故障注入技术通过字节码巩固方式贯彻,模拟常见的 RPC 故障,解决微服务的强弱正视治理难点。

MonkeyKing(2016-2018):故障演习平台的晋升版本,丰硕了故障场景(如:财富、容器层场景),开端在生产境况张开一些规模化的彩排。

AHAS(2018.9-至今):Ali云应用高可用服务,内置演习平台的整套意义,帮助可编写制定练习、演习插件扩充等力量,并整合了架构感知和限流降级的作用。

ChaosBlade:是 MonkeyKing 平台底层故障注入的贯彻工具,通过对演习平台底层的故障注入技巧开展抽象,定义了一套故障模型。同盟客户自个儿的 CLI 工具举行开源,协助云原生客商进行混沌工程测量检验。

澳门威斯尼斯人手机版 7

(1)加载字节码到内部存储器:(这一步平时经过findclass()方法达成)

以ULX570LClassLoader为例:该类的构造函数返现必需制订二个UQX56L数据手艺创设该目的,该类中含有贰个U路虎极光LClassPath对象,URLClassPath会判定传过来的UENVISIONL是文本只怕Jar包,创设相应的FileLoader或然JarLoader也许私下认可加载器,当jvm调用findclass时,这个加载器将class文件的字节码加载到内部存款和储蓄器中

AppClassLoader 的制造进度

理清了 ExtClassLoader 的创导进程,大家来看 AppClassLoader 的创造进度就明明白白相当多了。追踪 getAppClassLoader() 方法的调用进程,能够观望那么些法子自己将 ExtClassLoader 的实例作为参数字传送入,最终依旧调用了 java.net.URLClassLoader 的构造方法,将 ExtClassLoader 的实例作为父构造器 parent 参数值传入。所以那边我们又足以鲜明,AppClassLoader 的父构造器正是ExtClassLoader。

  • 系统强弱信赖混乱、弱信赖无降级;
  • 系统流量剧增,系统容积不足,未有限流熔断机制;
  • 硬件能源互连网出现难题影响系统运行,未有高可用的互联网架构。

近期设计

效果与利益迭代:

  • 抓实 JVM 练习场景,协助越来越多的 Java 主流框架,如 Redis,GRPC
  • 拉长 Kubernetes 练习场景
  • 扩展对 C 、Node.js 等利用的支撑
(2)Linking:验证与解析,包括3步:
  • <1>字节码验证

  • <2>类希图:谋算代表各样类中定义的字段、方法和兑现接口所需的数据结构

  • <3>剖析:这么些品级类装入器转入类所使用的别的类

怎么加载一个类

将一个 .class 字节码文件加载到 JVM 中造成一个 java.lang.Class 实例须求加载那一个类的类加载器及其具备的父级加载器共同参加产生,那至关心珍贵借使遵照「双亲委派原则」。

五光十色的主题素材,在这种复杂的信赖结构下被加大,二个依附30个SOA服务的系统,各类服务99.99%可用。99.99%的34遍方≈99.7%。0.3%意味一亿次呼吁会有3,000,00次破产,换算成时间概况每月有2个小时服务动荡。随着服务信赖数量的变多,服务不稳定的票房价值会呈指数性提升,这个难点最后都会转接为故障表现出来。

社区一同建设:

应接访谈 ChaosBlade@GitHub,参与社区一同创建,包蕴但不压制:

  • 架构划虚构计
  • 模块设计
  • 代码达成
  • Bug Fix
  • Demo样例
  • 文书档案、网址和翻译

本文小编:中亭

阅读原来的作品

正文来源云栖社区合营同伙“ Ali本领”,如需转发请联系原来的著小编。

(3)起头化class对象,推行静态伊始化器并在那阶段末尾初步化静态字段为暗中同意值

二老委派

当大家要加载一个应用程序 classpath 下的自定义类时,AppClassLoader 会首先查看自身是不是曾经加载过那个类,如果已经加载过则一贯再次回到类的实例,不然将加载职责委托给和睦的父加载器 ExtClassLoader。同样,ExtClassLoader 也会先查看本人是或不是曾经加载过那一个类,纵然已经加载过则直接再次回到类的实例,不然将加载职责委托给自个儿的父加载器 BootStrapClassLoader。

BootStrapClassLoader 收到类加载职务时,会首先检查本身是还是不是业已加载过这些类,假使已经加载则直接重返类的实例,不然在投机担任的加载路线下搜寻这些类并尝试加载。要是找到了那个类,则施行加载职分并赶回类实例,不然将加载职分交给 ExtClassLoader 去推行。

ExtClassLoader 一样也在友好负担的加载路线下搜寻这一个类并尝试加载。假设找到了这一个类,则实行加载职务并再次回到类实例,不然将加载义务交给 AppClassLoader 去奉行。

由于投机的父加载器 ExtClassLoader 和 BootStrapClassLoader 都未能成功加载到这些类,所以最后由 AppClassLoader 来尝试加载。同样,AppClassLoader 会在 classpath 下全数的类库中追寻那么些类并尝试加载。假使最终依旧未有找到这几个类,则抛出 ClassNotFoundException 异常。

综上,当类加载器要加载贰个类时,假设协和一度未有加载过那么些类,则少有提升委托给父加载器尝试加载。对于 AppClassLoader 来说,它上边有 ExtClassLoader 和 BootStrapClassLoader,所以大家誉为「双亲委派」。不过一旦大家是使用自定义类加载器来加载类,且这个自定义类加载器的私下认可父加载器是 AppClassLoader 时,它下面就有多个父加载器,那时再说「双亲」就不太合适了。当然,驾驭了加载一个类的全数工艺流程,这么些名字就非亲非故痛痒了。

二、系统高可用的方法论

4.布满加载类错误深入分析

怎么必要家长江水利委员会派机制

「双亲委派机制」最大的裨益是防止自定义类和骨干类库争持。比如大家大批量接纳的 java.lang.String 类,假设大家和好写的一个 String 类被加载成功,那对于利用系统的话完全部都以毁灭性的毁伤。大家得以品味着写贰个自定义的 String 类,将其包也安装为 java.lang

package java.lang;public class String {    private int n;    public String {        this.n = n;    }    public String toLowerCase() {        return new String(this.n   100);    }}

大家将其创制成多少个 jar 包,命名叫 thief-jdk,然后写三个测量试验类尝试加载 java.lang.String 并动用抽出三个 int 类型参数的构造方法创设实例。

import java.lang.reflect.Constructor;public class Test {    public static void main(String[] args) throws Exception {        Class<?> clz = Class.forName("java.lang.String");        System.out.println(clz.getClassLoader() == null);        Constructor<?> c = clz.getConstructor(int.class);        String str =  c.newInstance;        str.toLowerCase();    }}

运作测验程序

java -cp /Users/yu/Desktop/lib/thief/thief-jdk.jar:. Test

程序抛出 NoSuchMethodException 极度,因为 JVM 不能加载大家自定义的 java.lang.String,而是从 BootStrapClassLoader 的缓存中回到了宗旨类库中的 java.lang.String 的实例,且基本类库中的 String 未有吸取 int 类型参数的构造方法。同期大家也看看 Class 实例的类加载器是 null,那也说明了作者们获得的 java.lang.String 的实例确实是由 BootStrapClassLoader 加载的。

简单来讲,「双亲委派」机制的法力正是确认保障类的唯一性,最直白的事例正是幸免大家自定义类和基本类库争辨。

什么构建八个高可用的系统吧?首先要分析一下不可用的因素都有如何:

(1)ClassNotFoundException:

通常是jvm要加载一个文书的字节码到内部存储器时,没有找到这个字节码(如forName,loadClass等办法)

JVM 怎么剖断三个类是同等的

「双亲委派」机制用来保障类的独一性,那么 JVM 通过怎么着典型来决断独一性呢?其实很简短,只要五个类的全路线名称同样,且都以同贰个类加载器加载,那么就推断这两个类是同样的。假如一样份字节码被分歧的两个类加载器加载,那么它们就不会被 JVM 剖断为同一个类。

Person 类

public class Person {    private Person p;    public void setPerson(Object obj) {        this.p =  obj;    }}

setPerson(Object obj) 方法接收三个对象,并将其挟持转变为 Person 类型赋值给变量 p。

测试类

import java.lang.reflect.Method;public class Test {    public static void main(String[] args) {        CustomClassLoader classLoader1 = new CustomClassLoader("/Users/yu/Desktop/lib");        CustomClassLoader classLoader2 = new CustomClassLoader("/Users/yu/Desktop/lib");        try {            Class c1 = classLoader1.findClass("Person");            Object instance1 = c1.newInstance();            Class c2 = classLoader2.findClass("Person");            Object instance2 = c2.newInstance();            Method method = c1.getDeclaredMethod("setPerson", Object.class);            method.invoke(instance1, instance2);        } catch (Exception e) {            e.printStackTrace();        }    }}

CustomClassLoader 是三个自定义的类加载器,它将字节码文件加载为字符数组,然后调用 ClassLoader 的 defineClass() 方法创建类的实例,后文少禽详细批注怎么自定义类加载器。在测验类中,我们成立了八个类加载器的实例,让她们分别去加载同一份字节码文件,即 Person 类的字节码。然后在实例一上调用 setPerson() 方法将实例二传入,将实例二威迫转型为实例一。

运维程序拜谒到 JVM 抛出了 ClassCastException 分外,非凡消息为 Person cannot be cast to Person。从那大家就能够精晓,同一份字节码文件,假如应用的类加载器区别,那么 JVM 就能剖断他们是不一致的种类。

澳门威斯尼斯人手机版 8

(2)NoClassDefFoundError:

一般来讲是运用new关键字,属性引用了有个别类,承袭了有个别类或接口,但JVM加载那么些类时开采那些类不设有的丰硕

一心担任

「全盘担任」是类加载的另一个条件。它的意思是要是类 A 是被类加载器 X 加载的,那么在平素不呈现内定别的类加载器的情事下,类 A 援引的别的兼具类都由类加载器 X 负担加载,加载进度遵守「双亲委派」原则。我们编辑七个类来验证「全盘担任」原则。

Worker 类

package com.ganpengyu.full;import com.ganpengyu.boot.DateUtils;public class Worker {    public Worker() {    }    public void say() {        DateUtils dateUtils = new DateUtils();        System.out.println(dateUtils.getClass().getClassLoader() == null);        dateUtils.printNow();    }}

DateUtils 类

package com.ganpengyu.boot;import java.text.SimpleDateFormat;import java.util.Date;public class DateUtils {    public void printNow() {        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        System.out.println(sdf.format(new Date;    }}

测试类

import com.ganpengyu.full.Worker;import java.lang.reflect.Constructor;public class Test {    public static void main(String[] args) throws Exception {        Class<?> clz = Class.forName("com.ganpengyu.full.Worker");        System.out.println(clz.getClassLoader() == null);        Worker worker =  clz.newInstance();        worker.say();    }}

运转测验类

java -Xbootclasspath/a:/Users/yu/Desktop/lib/worker.jar Test

运作结果

truetrue2018-09-16 22:34:43

大家将 Worker 类和 DateUtils 类制作成名称叫worker 的 jar 包,将其安装为由根加载器加载,那样 Worker 类就决然是被根加载器加载的。然后在 Worker 类的 say() 方法中早先化了 DateUtils 类,然后判定 DateUtils 类是或不是由根加载器加载。从运维结果来看,Worker 和其引述的 DateUtils 类都被跟加载器加载,符合类加载的「全盘委托」原则。

「全盘委托」原则实际是为「双亲委派」原则提供了确定保障。假诺不遵从「全盘委托」原则,那么相同份字节码可能会被 JVM 加载出多少个不等的实例,那就能够促成应用系统中对该类援引的繁杂,具体能够参照他事他说加以考察上文「JVM 怎么判定三个类是平等的」这一节的示范。

高可用系统特出实行

(3)UnsatisfiedLinkErrpr:

如native的方式找不到本机的lib

自定义类加载器

除去利用 JVM 预订义的三种类加载器外,Java 还允许大家自定义类加载器以让我们系统的类加载格局越来越灵活。要自定义类加载器非常轻便,平常只须求三个步骤:

  1. 继承 java.lang.ClassLoader 类,让 JVM 知道这是二个类加载器
  2. 重写 findClass(String name) 方法,告诉 JVM 在选择这一个类加载器时应该按如何方法去探究 .class 文件
  3. 调用 defineClass(String name, byte[] b, int off, int len) 方法,让 JVM 加载上一步读取的 .class 文件
import java.io.*;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;public class CustomClassLoader extends ClassLoader {    private String classpath;        public CustomClassLoader(String classpath) {        this.classpath = classpath;    }    @Override    protected Class<?> findClass(String name) throws ClassNotFoundException {        String classFilePath = getClassFilePath;        byte[] classData = readClassFile(classFilePath);        return defineClass(name, classData, 0, classData.length);    }    public String getClassFilePath(String name) {        if (name.lastIndexOf(".") == -1) {            return classpath   "/"   name   ".class";        } else {            name = name.replace(".", "/");            return classpath   "/"   name   ".class";        }    }    public byte[] readClassFile(String filepath) {        Path path = Paths.get;        if (!Files.exists {            return null;        }        try {            return Files.readAllBytes;        } catch (IOException e) {            throw new RuntimeException("Can not read class file into byte array");        }    }    public static void main(String[] args) {        CustomClassLoader loader = new CustomClassLoader("/Users/leon/Desktop/lib");        try {            Class<?> clz = loader.loadClass("com.ganpengyu.demo.Person");            System.out.println(clz.getClassLoader().toString;            Constructor<?> c = clz.getConstructor(String.class);            Object instance = c.newInstance("Leon");            Method method = clz.getDeclaredMethod("say", null);            method.invoke(instance, null);        } catch (Exception e) {            e.printStackTrace();        }    }}

演示中大家透过承继 java.lang.ClassLoader 创立了二个自定义类加载器,通过构造方法钦点那个类加载器的类路线(classpath)。重写 findClass(String name) 方法自定义类加载的措施,个中 getClassFilePath(String filepath) 方法和 readClassFile(String filepath) 方法用于找到钦定的 .class 文件并加载成二个字符数组。最后调用 defineClass(String name, byte[] b, int off, int len) 方法成功类的加载。

main() 方法中大家测试加载了三个 Person 类,通过 loadClass(String name) 方法加载贰个 Person 类。我们自定义的 findClass(String name) 方法,便是在这里面调用的,大家把那么些艺术轻便展示如下:

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {    synchronized (getClassLoadingLock {        // 先检查是否已经加载过这个类        Class<?> c = findLoadedClass;        if (c == null) {            long t0 = System.nanoTime();            try {                // 否则的话递归调用父加载器尝试加载                if (parent != null) {                    c = parent.loadClass(name, false);                } else {                    // 所有父加载器都无法加载,使用根加载器尝试加载                    c = findBootstrapClassOrNull;                }            } catch (ClassNotFoundException e) {}            if (c == null) {                // 所有父加载器和根加载器都无法加载                // 使用自定义的 findClass() 方法查找 .class 文件                c = findClass;            }        }        return c;    }}

能够看出 loadClass(String name) 方法内部是根据「双亲委派」机制来成功类的加载。在「双亲」都未能成功加载类的事态下才调用大家自定义的 findClass(String name) 方法找寻目的类施行加载。

答辩上来讲,当图中全数的业务都做完,大家就足以以为系统是三个当真的高可用系统。但真是这样吗?

5.常用classLoader(书本此处其实是对tom加载servlet使用的classLoader分析)

为什么供给自定义类加载器

自定义类加载器的用途有许多,这里大约列举部分普遍的景观。

  1. 从随飞机地点置加载类。JVM 预订义的八个类加载器都被界定了上下一心的类路线,大家得以由此自定义类加载器去加载其余随便地点的类。
  2. 解密类文件。比如大家可以对编写翻译后的类公事举行加密,然后通过自定义类加载器进行解密。当然这种艺术其实并从未太大的用途,因为自定义的类加载器也得以被反编写翻译。
  3. 帮助越来越灵敏的内部存储器管理。大家能够动用自定义类加载器在运营时卸载已加载的类,进而更急迅的应用内部存款和储蓄器。

那么故障练习平台就热火朝天上台了。当上述的高可用实行都做完,利用故障演练平台做二次真正的故障演习,在系统运维期动态地注入一些故障,进而来注解下系统是不是比照故障预案去实践相应的降级恐怕熔断计谋。

(1)AppClassLoader:

加载jvm的classpath中的类和tomcat的宗旨类

就那样吗

类加载器是 Java 中极度焦点的本事,本文仅对类加载器进行了较为通俗的分析,要是要求深入更底层则必要大家开采JVM 的源码举办研读。「Java 有路勤为径,JVM 无涯苦作舟」,与君共勉。

三、故障练习平台

(2)StandardClassLoader:

加载tomcat容器的classLoader,别的webAppClassLoader在loadclass时,开掘类不在JVM的classPath下,在PackageTriggers(是叁个字符串数组,包涵一组不能够应用webAppClassLoader加载的类的包名字符串)下的话,将由该加载器加载(注意:StandardClassLoader并不曾覆盖loadclass方法,所以其加载的类和AppClassLoader加载没什么分别,而且动用getClassLoader再次来到的也是AppClassLoader)(别的,若是web应用直接放在tomcat的webapp目录下该利用就能够由此StandardClassLoader加载,预计是因为webapp目录在PackageTriggers中?)

故障操练平台:检查故障预案是或不是确实的起作用的阳台。

(3)webAppClassLoader如:

Servlet等web应用中的类的加载(loadclass方法的准则详见P169)

故障类型:要害包罗运行期非凡、超时等等。通过对系统有个别服务动态地注入运维期卓殊来到达模拟故障的目标,系统根据预案试行相应的国策验证系统是或不是是真正的高可用。

6.自定义的classloader

1、故障练习平台的一体化框架结构

(1)要求运用自定义classloader的气象
  • <1>不在System.getProperty("java.class.path")中的类公事不得以被AppClassLoader找到(LoaderClass方法只会去classpath下加载特定类名的类),当class文件的字节码不在ClassPath就需求自定义classloader

  • <2>对加载的有个别类要求作极度管理

  • <3>定义类的实际效果机制,对已经修改的类重新加载,实现热布置

故障练习平台架构首要分为四片段:

(2)加载自定义路线中的class文件
  • <1>加载特定来源的一点类:重写find方法,使特定类或然特定来源的字节码 通过defineClass得到class类并回到(应该符合jvm的类加载标准,别的类仍利用父加载器加载)

  • <2>加载自顶四个是的class文件(如通过网络传遍的经过加密的class文件字节码):findclass中加密后再加载

澳门威斯尼斯人手机版 9

7.达成类的热安顿:

  • (1)同二个classLoader的七个实例加载同二个类,JVM也会识别为八个

  • 澳门威斯尼斯人手机版:七年打磨,深刻剖判ClassLoader加运载飞机制。(2)不能再度加载同八个类(全名一样,并动用同贰个类加载器),会报错

  • (3)不应有动态加载类,因为对象呗引用后,对象的本性结构被涂改会引发难点

瞩目:使用不一样classLoader加载的同贰个类公事获得的类,JVM将作为是多个不一致类,使用单例方式,强制类型转变时都恐怕因为那个缘故出标题。

  • 前台体现系统(WEB):呈现系统里头的拓扑关系以及各样AppCode对应的集群和格局,可以挑选具体的办法开展故障的注入和消除;
  • 公布系统(Deploy):其一系统第一用以将故障演习平台的Agent和Binder包发布到对象APP的机械上还要运维实施。前台体现系统会传送给发表平台要扩充故障注入的AppCode以及指标APP的IP地址,通过那么些参数公布体系能够找到相应的机器进行Jar包的下载和起步;
  • 服务和指令分发系统(Server):其一连串第一是用于命令的分发、注入故障的景观记录、故障注入和清除操作的逻辑、权限校验以及相关的Agent的回来音信接收效果。前台页面已经接入QSSO会对当前人能够操作的IP列表做故障注入,堤防危机。后端命令分发的模块会和配备在对象APP上的Agent进行通讯,将指令推送到Agent上施行字节码编织,Agent施行命令后归来的源委通过Server和Agent的长连接传回Server端;
  • Agent和Binder程序:Agent肩负对目的应用软件做代办并且做字节码巩固,具体代理的艺术可以经过传输的一声令下来调整,代理方法后对艺术做动态的字节码巩固,这种字节码加强全体无侵入、实时生效、动态可插拔的风味。Binder程序首即使由此公布系统传递过来的AppCode和开发银行端口(ServerPort)找到对象应用程式的JVM进度,之后实施动态绑定,完结运维期代码加强的效果与利益。

原书链接

如上内容只是个体笔记纪录,越来越多完整内容请购买小编原书籍查看。《深切深入分析JavaWeb手艺内部原因》

2、 Agent全部架构

此时此刻AOP的兑现存三种艺术:

  • 静态编织:静态编织产生在字节码生成时依照早晚框架的条条框框提前将AOP字节码插入到指标类和办法中;
  • 动态编织:在JVM运转期对内定的方法成功AOP字节码加强。常见的法子大好多选取重命名原有艺术,再新建三个同名方法做代办的行事方式来完毕。

静态编织的主题材料是假使想退换字节码必须重启,那给支付和测验进程导致了相当大的困顿。动态的艺术固然能够在运转期注入字节码实现动态增进,但绝非洲统一组织一的API很轻巧操作不当。基于此,我们运用动态编织的点子、标准的API来规范字节码的浮动——Agent组件。

Agent组件:透过JDK所提供的Instrumentation-API达成了利用HotSwap技艺在不重启JVM的动静下促成对自由方法的增加,无论大家是做故障练习、调用链追踪(QTrace)、流量摄像平台(Ares)以及动态扩充日志输出BTrace,都急需一个持有无侵入、实时生效、动态可插拔的字节码加强组件。

Agent的事件模型

如图所示,事件模型主要可分为三类事件:

澳门威斯尼斯人手机版 10

BEFORE在章程施行前事件、THROWS抛出十三分事件、RETUEnclaveN再次来到事件。那三类事件能够在情势试行前、再次回到和抛出十分那三种意况做字节码编织。

正如代码:

// BEFORE

try {

/*

* do something...

*/

foo();

// RETURN

return;

} catch (Throwable e) {

// THROWS

}

事件模型能够做到四个作用:

  • 在方法体实践在此之前平昔回到自定义结果对象,原有办法代码将不会被施行;
  • 在方法体再次来到从前再度布局新的结果对象,以至能够退换为抛出十一分;
  • 在方法体抛出非常之后重新抛出新的非常,以致能够转移为常规再次回到。

Agent怎样防止“类污染”

在开采Agent的时候,第4个使用是故障演习平台,那么今年实在大家并没有须要Agent实行的长河中有自定义结果对象的回来,所以首先个版本的Agent采纳硬编码的不二等秘书籍进行动态织入:

澳门威斯尼斯人手机版 11

故障类加载模型

率先介绍下多少个类加载器:

  • BootstrapClassLoader辅导类加载器加载的是JVM自个儿须求的类,这一个类加载使用C 语言完毕的,是设想机自己的一部分;
  • ExtClassLoader它担任加载<JAVA_HOME>/lib/ext目录下只怕由系统变量-Djava.ext.dir钦命位路线中的类库;
  • AppClassLoader它担负加载系统类路线java-classpath或-D java.class.path钦点路径下的类库,也便是大家平常应用的classpath路线;
  • CommonClassLoader以及上边的都以汤姆cat定义的ClassLoader。

Agent和相关的lib会放到AppClassLoader这一层去加载,利用Javasist做字节码的织入,所以Javasist的加载器正是AppClassLoader。

不过想更换的是汤姆cat WebClassLoader所加载的com.xxx.InvocationHandler这几个类的Invoke方法,区别的ClassLoader之间的类是不可能相互访问的,做字节码的转换并不供给那一个类的实例,也无需回到结果,所以能够通过Instrument API得到那个类加载器,何况能够根据类名称获取到这一个类的字节码实行字节码转变。故障类Drill.class和变形后的com.xxx.InvocationHandler.class重新load到JVM中,达成了插桩操作。

以Dubbo为例表达下何以注入故障和排除故障:

澳门威斯尼斯人手机版 12

Dubbo调用的注入进程

  • 服务A调用服务B在Client端的Proxy层做AOP;
  • 起步Agent並且生成叁个Drill类invoke方法,抛出二个运转期非常;
  • 字节码变形:在代码第一行在此以前扩充Drill.invoke();
  • 假定想改变卓殊类型,改变Drill类就可以,换来Sleep 3s ClassRedifine从此会再也load到JVM实现故障类型的转向恐怕免除。

相见的标题

下边包车型地铁不二诀窍相似很圆满的缓慢解决了难点,不过随着平台的运用职业线要对众多接口和章程同时拓宽故障练习,那么大家调换的Drill类里面就能有各个:

if method==业务线定义方法

do xxx

同时很轻易拼接出错何况难以调节和测量试验,只可以把调换的类输出为文件,查看自身写的字节码编写翻译成class文件是不是准确,大致太优伤了!

怎么化解?

新的架构须要缓和四个难点:

  • 类隔开分离的主题材料:不要污染原生应用软件;
  • 事件的贯彻是可编写翻译的;
  • 援助回到自定义的结果。

下一版本的Agent完毕就时有发生了,把持有Agent的类和兑现的机能抽象出来,放到三个自定义的AgentClassLoader里面,字节码注入到指标APP后能够透过反射的方法来调用具体的平地风波实现。

澳门威斯尼斯人手机版 13

类加载模型

  • 在BootstrapClassLoader里面注入Drill类作为通讯类;
  • Agent会接受命令,依照事件类型对InvocationHandler做字节码变形,注入到对象应用程式;
  • 在对象APP调用的时候,调用Drill.invoke(targetJavaClass,targetJavaMethod, targetThis, args)传递过来多少个参数(目的类、方法、实例、本黄参数等);
  • Drill类通过反射的格局调用AppClassLoader里面包车型大巴现实性事件落成,譬喻BEFORE事件的试行代码,来完成注入后的逻辑实践。

Agent的完全框架结构

Agent的总体架构如图所示:

澳门威斯尼斯人手机版 14

  • 帮忙分裂的模块的投入,比如Mock、流量摄像、故障演练等;
  • 补助QSSO的权柄验证;
  • 帮忙测验和虚伪情况的无资金接入;
  • 支持电动安插没有要求人工加入;
  • 支撑各类故障命令的发表和试行、 超时 、至极以及数额的归来;
  • 帮忙艺术级其余编写制定以及代码施行流程的编织;
  • 支撑在恣心纵欲的Web容器实行Agent代理。

四、怎样使用

选择的好处是很分明的:

  • 零开支接入,没有要求申请其余财富;
  • 故障注入解除,无需重启服务;
  • 能够提供具备集群的拓扑结构。

但是怎么开头艺准确运用啊?如下图所示:

澳门威斯尼斯人手机版 15

利用办法

步骤一、输入AppCode;

手续二、选取故障方法;

步骤三、钦命机器;

澳门威斯尼斯人手机版:七年打磨,深刻剖判ClassLoader加运载飞机制。步骤四、注入故障。

五、总结

故障练习平台最焦点的正是Agent组件——字节码编织框架,那几个框架是纯Java的遵照Instrumentation-API的AOP技术方案。它能够一本万利研究开发人员对此字节码插桩拆桩操作,能够很轻便的贯彻故障演习、流量摄像以及别的的施用模块。

作者:王鹏

源于:Qunar才能沙龙订阅号(ID:QunarTL)

dbaplus社会群众体育招待广大技术职员投稿,投稿邮箱:editor@dbaplus.cn回来乐乎,查看更加多

小编:

本文由澳门威斯尼斯人手机版发布于互联网科技,转载请注明出处:澳门威斯尼斯人手机版:七年打磨,深刻剖判C

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。