原标题:去什么地方系统高可用之法:搭建故障演练平台

Classloader负责将Class加载到JVM中,并且规定由尤其ClassLoader来加载(父优先的等级加载机制)。还有二个职分就是将Class字节码重新诠释为JVM统一须要的格式

agent有两种: native(jvmti接口) 和 java层面的(instrumentation)

我介绍

1.Classloader类结构分析

  • c/c++ 层面的 jvmti 接口

    • jvmti官方文档
    • JVM TI是JDK提供的一套用于支付JVM监控,
      难点一定与质量调优工具的通用编程接口(API)。
      通过JVMTI,大家可以付出各样种种的JVMTI
      Agent。那几个Agent的表现方式是三个以c/c++语言编写的动态共享库
    • JVMTI Agent原理
      • Java运营或运营时,动态加载叁个表面基于JVM TI编写的dynamic
        module到Java进度内,然后触发JVM源生线程Attach
        Listener来执行这些dynamic
        module的回调函数。在函数体内,你能够拿到种种各种的VM级音讯,注册感兴趣的VM事件,甚至决定VM的行事。
    • jvmti api
      • JVMTI是依据事件驱动的,JVM每执行到早晚的逻辑就会调用一些轩然大波的回调接口(若是局地话),那一个接口可以供开发者去扩张自个儿的逻辑。
      • 可以取得各样各个的音讯
    • 支付jvm ti
      agent,不难的来讲,就是支付3个c/c++的共享库。在windows下后缀是dll,linux/unix下是so,mac下就是dylib。所以大家创立工程和编译环境的时候,记得以共享库(share
      library)的样式来构建
    • 三种方式载入
      • 随java进度运营时,自动载入共享库
        • 共享库路径是环境变量路径: java
          -agentlib:foo=opt1,opt2,java运营时会从linux的LD_LIBRARY_PATH或windows的PATH环境变量定义的路径处装载foo.so或foo.dll,找不到则抛万分
        • 以相对路径的点子装载共享库: java
          -agentpath:/home/admin/agentlib/foo.so=opt1,opt2 山姆ple
      • java运维时,通过attach api动态载入
        public static void main(String[] args) throws Exception { // args[0]为java进程id VirtualMachine virtualMachine = com.sun.tools.attach.VirtualMachine.attach(args[0]); // args[1]为共享库路径,args[2]为传递给agent的参数 virtualMachine.loadAgentPath(args[1], args[2]); virtualMachine.detach(); }
    • 开发jvmti agent
      • jvmti.h头文件里带有了富有jvm
        ti要用到的数据结构和回调函数定义
      • 分明JVMTI的启航格局
        • JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM vm, char
          options, void *reserved)//运转载入方式
        • JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM jvm, char
          options, void *reserved)//动态载入形式
        • JNIEXPORT void JNICALL Agent_OnUnload(JavaVM
          *vm)//卸载都以千篇一律
      • 实际事例可以google或jvmti官网上找
  • java层面的instrumentation

    • Instrumentation是Java5提供的新性子,使用Instrumentation,开发者可以打造一个代理,用来监测运转在JVM上的先后

    • java.lang.instrument.ClassFileTransformer

      • 每一种代理类必须兑现
        ClassFileTransformer接口,那几个接口提供了多少个transform方法:
      • byte[] transform(ClassLoader loader, String className,
        Class<?> classBeingRedefined, ProtectionDomain
        protectionDomain, byte[] classfileBuffer) throws
        IllegalClassFormatException
      • 因此那些点子,代理可以博得虚拟机载入的类的字节码,并可对其进展改动,完结字节码级的改动。
      • classfileBuffer那一个便是被代理类字节码流,正是经过操作那几个buffer完结对字节码的修改
      • 对此函数的再次回到值,假设回到null,则意味着不对类的字节码做别的的改动,否则应当回到修改过的byte[]对象
    • 提供多个国有的静态方法 public static void premain(String
      agentArgs, Instrumentation inst)

      • 一般会在这一个艺术中开创贰个代理对象,通过Instrumentation对象的addTransformer()方法,将创制的代办对象再传递给虚拟机
        public class HelloWorld implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class<>; classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { System.out.println("java.lang.instrument, hello world!"); return null; } public static void premain(String args,Instrumentation inst){ inst.addTransformer(new HelloWorld()); } }

      public class Example { public static void main(String[] args){ System.out.println("main class of proxy!"); } }

      • 将agent类HelloWorld编译成可运营的jar(helloworld.jar),那里注意在manifest文件中添加premain入口,也即其安排为:
        • Premain-Class: cn.dstrace.instrument.HelloWorld
      • 运行
        • java -javaagent:helloworld.jar
          cn.dstrace.instrument.Example
    • 小节

      • java的种种质量监控工具中都有instrument的人影,如jconsole等
      • 这么的性状实际上提供了一种虚拟机级别襄助的 AOP 落成格局
    • 动态的javaagent

      • 在 Java SE6 里面,最大的改变使运转时的 Instrumentation
        成为只怕;java attach api
      • 不过在实质上的重重的情景下,大家从不办法在虚拟机运维之时就为其设定代理
      • jdk5局限
        • 在 Java SE 5 中级,开发者可以让 Instrumentation 代理在
          main 函数运转前履行。
        • 在 Java SE 5 中路,开发者只可以在 premain
          当中施展想象力,所作的 Instrumentation 也仅限与 main
          函数执行前,那样的不二法门存在一定的局限性。
        • 在 Java SE 5 的底子上,Java SE 6
          针对那种光景做出了校对,开发者可以在main
          函数开端实践今后,再开行自个儿的 Instrumentation 程序。
        • 在 Java SE 6 的 Instrumentation 当中,有一个跟
          premain“齐足并驱”的“agentmain”方法,能够在 main
          函数开端运转之后再运转。跟 premain 函数一样,
          开发者可以编写1个分包“agentmain”函数的 Java 类:
        • 在 Java SE 6
          的新特征里面,有多少个不太起眼的地点,揭发了 agentmain
          的用法。那就是Java SE 6 当中提供的 Attach API
  • javaagent原理完全解读

    • javaagent的机要的效率如下
      • 可以在加载class文书此前做阻止把字节码做修改
      • 可以在运行期将已经加载的类的字节码做改变,但是那种情景下会有众多的限定
      • 还有此外的一些小众的机能
        • 赢得具有曾经被加载过的类
        • 收获具有曾经被伊始化过了的类(执行过了clinit方法,是地点的四个子集)
        • 赢得某些对象的分寸
        • 将有个别jar加入到bootstrapclasspath里当作高优先级被bootstrapClassloader加载
        • 将某个jar加入到classpath里供AppClassloard去加载
        • 安装有些native方法的前缀,主要在检索native方法的时候做规则匹配
    • 实在大家每一日都在和JVMTIAgent打交道,只是你只怕没有察觉到而已,比如大家日常应用eclipse等工具对java代码做调试,其实就使用了jre自带的jdwp
      agent来促成的,只是出于eclipse等工具在没让你意识的情状下将相关参数(类似-agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:61349)给电动加到程序运转参数列表里了,其中agentlib参数就是用来跟要加载的agent的名字,比如此处的jdwp(但是那不是动态库的名字,而JVM是会做一些称号上的恢宏,比如在linux下会去找libjdwp.so的动态库进行加载,约等于在名字的基础上加前缀lib,再加后缀.so),接下去会跟一堆相关的参数,会将这一个参数传给Agent_OnLoad或者Agent_OnAttach函数里对应的options参数。
    • javaagent
      • 说到javaagent必须求讲的是一个称呼instrument的JVMTIAgent(linux下相应的动态库是libinstrument.so),因为就是它来落到实处javaagent的职能的,除此以外instrument
        agent还有各自名叫JPLISAgent
        (Java Programming Language
        Instrumentation Services
        Agent),从这名字里也截然反映了其最本质的效益:就是特地为java语言编写的插桩服务提供支撑的。
    • instrument agent (libinstrument.so实现)
      • instrument agent实现了Agent_OnLoad和Agent_OnAttach两方法
      • instrument agent的宗旨数据结构如下
        • tobecontinued…

  • References

    • 笨神-JVM源码分析之javaagent原理完全解读

王鹏,二〇一七年加入去何地机票事业部,首要从事后端研发工作,方今在机票事业部负责行程单和故障演练平台以及公共服务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那几个类?)

去何方网二零零五年确立于今,随着系统规模的日益伸张,已经有众四个应用连串,这一个连串里面的耦合度和链路的复杂度不断狠抓,对于我们打造分布式高可用的连串架构具有巨大挑衅。大家须要1个平台在运营期自动注入故障,检验故障预案是不是起效——故障演练平台。

(2)完成自定义ClassLoader一般会继续UEnclaveLClassLoader类,因为这几个类已毕了大多数办法。

一、背景

2.ClassLoader的等级加载机制

这是某事业部的系统拓扑图:

(1)JVM平台提供三层的ClassLoader,那三层ClassLoader可以分成两类,分别是服务JVM自身的,和劳务周边普通类的。分别是:
  • <1>BootstrapClassLoader:首要加载JVM本身工作所须求的类,该ClassLoader没有父类加载器和子类加载器

  • <2>ExtClassLoader:那个类加载器同样是JVM自个儿的一局地,可是否由JVM完结,主要用以加载System.getProperty(“java.ext.dirs”)目录地下的类,如本机的值“D:\java\jdk7\jre\lib\ext;C:\Windows\Sun\Java\lib\ext”

  • <3>AppClassLoader:加载System.getProperty(“java.class.path”)(注意了在ide中运维程序时,该值寻常是该品种的classes文件夹)中的类。全体的自定义类加载器不管直接促成ClassLoader,是两次三番自UTiguanLClassLoader或其子类,其父加载器(注意:父加载器与父类的分别)都是AppClassLoader,因为随便调用哪个父类的构造器,最后都将调用getSystemClassLoader作为父加载器,而该方式再次来到的正是AppClassLoader。(当应用程序中从不其余自定义的classLoader,那么除了System.getProperty(“java.ext.dirs”)目录中的类,其他类都由AppClassLoader加载)

永利游戏网址 1

(2)Jvm加载class文件到内有所两种方法,隐式加载和突显加载,寻常那三种艺术是参差不齐使用的
  • <1>隐式加载:是透过JVM来自动加载须求的类到内存的主意,当某些类被运用时,JVM发现此类不在内存中,那么它就会自动加载该类到内存

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

系统里面的依靠相当复杂、调用链路很深、服务中间从未分支。在那种复杂的尊崇下,系统发出了几起故障:

(3)上级委托机制:当一个加载器加载类字时,先委托其父加载器加载,若加载成功则反映给该加载器,若父加载器不可以加载,则由该加载器加载
  • 弱倚重挂掉,主流程挂掉,修改报废凭据的成本情形,下单主流程战败;
  • 主干服务调用量陡增,某服务超时引起相关联的有所服务“雪崩”;
  • 机房网络或许某个机器挂掉,不可能提供基本服务。

3.什么加载class文件:

分成多少个步骤 加载字节码到内存、Linking、类字节开始化赋值

七个故障原因:

(1)加载字节码到内存:(这一步寻常通过findclass()方法完结)

以U奇骏LClassLoader为例:该类的构造函数返现必须制定1个U福特ExplorerL数据才能创立该目的,该类中涵盖3个U途睿欧LClassPath对象,U途乐LClassPath会判断传过来的UCRUISERL是文件或然Jar包,创设相应的FileLoader或然JarLoader或然暗中同意加载器,当jvm调用findclass时,那几个加载器将class文件的字节码加载到内存中

  • 系统强弱器重混乱、弱依赖无降级;
  • 系统流量剧增,系统容量不足,没有限流熔断机制;
  • 硬件财富网络出现难点影响系统运维,没有高可用的互连网架构。
(2)Linking:验证与分析,包罗3步:
  • <1>字节码验证

  • <2>类准备:准备代表每一种类中定义的字段、方法和促成接口所需的数据结构

  • <3>解析:那一个等级类装入器转入类所拔取的别的类

充足多彩的标题,在那种复杂的重视结构下被推广,八个凭借三十个SOA服务的系统,各种服务99.99%可用。99.99%的贰十五次方≈99.7%。0.3%意味一亿次呼吁会有3,000,00次破产,换算成时间大约每月有三个时辰服务不安宁。随着服务器重数量的变多,服务不安定的几率会呈指数性进步,那几个标题最后都会转接为故障表现出来。

(3)初始化class对象,执行静态起始化器并在那阶段末尾开端化静态字段为专擅认同值

二 、系统高可用的方法论

4.周边加载类错误分析

怎么着创设叁个高可用的种类啊?首先要分析一下不可用的要素都有怎么着:

(1)ClassNotFoundException:

一般说来是jvm要加载一个文本的字节码到内存时,没有找到那么些字节码(如forName,loadClass等办法)

永利游戏网址 2

(2)NoClassDefFoundError:

常常是行使new关键字,属性引用了某些类,继承了有个别类或接口,但JVM加载这一个类时发现这么些类不存在的老大

高可用系统独立实践

(3)UnsatisfiedLinkErrpr:

如native的主意找不到本机的lib

答辩上的话,当图中全部的业务都做完,大家就足以认为系统是2个当真的高可用系统。但真是如此呢?

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

那么故障演练平台就热闹特出登场了。当上述的高可用实践都做完,利用故障演练平台做一遍真正的故障演练,在系统运维期动态地注入一些故障,从而来验证下系统是不是比照故障预案去实践相应的降级大概熔断策略。

(1)AppClassLoader:

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

三 、故障演练平台

(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)须求动用自定义classloader的景色
  • <1>不在System.getProperty(“java.class.path”)中的类公事不可以被AppClassLoader找到(LoaderClass方法只会去classpath下加载特定类名的类),当class文件的字节码不在ClassPath就需求自定义classloader

  • <2>对加载的某个类须求作尤其处理

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

故障演练平台架构主要分为四有的:

(2)加载自定义路径中的class文件
  • <1>加载特定来源的一点类:重写find方法,使特定类大概特定来源的字节码
    通过defineClass得到class类并赶回(应该符合jvm的类加载规范,其他类仍利用父加载器加载)

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

永利游戏网址 3

7.达成类的热安顿:

  • (1)同3个classLoader的多少个实例加载同1个类,JVM也会识别为五个

  • (2)不能够再次加载同1个类(全名相同,并应用同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负责对目标APP做代理并且做字节码增强,具体代理的方式可以由此传输的吩咐来支配,代理方法后对章程做动态的字节码增强,那种字节码增强全部无侵入、实时生效、动态可插拔的特点。Binder程序重即使通过公告系统传递过来的AppCode和运转端口(ServerPort)找到对象APP的JVM进度,之后执行动态绑定,落成运维期代码增强的成效。

原书链接

如上内容只是个体笔记纪录,更加多完整内容请购买小编原书籍查看。《深远剖析JavaWeb技术内幕》

② 、 Agent全体架构

此时此刻AOP的落到实处有三种格局:

  • 静态编织:静态编织暴发在字节码生成时根据早晚框架的平整提前将AOP字节码插入到目的类和形式中;
  • 动态编织:在JVM运维期对钦命的办法成功AOP字节码增强。常见的点子超过二分之一拔取重命名原有办法,再新建3个同名方法做代理的行事格局来形成。

静态编织的问题是假如想改变字节码必须重启,那给开发和测试进程导致了非常的大的勤奋。动态的点子固然可以在运转期注入字节码完毕动态拉长,但尚未统一的API很简单操作错误。基于此,大家利用动态编织的措施、规范的API来规范字节码的浮动——Agent组件。

Agent组件:透过JDK所提供的Instrumentation-API落成了应用HotSwap技术在不重启JVM的景况下完成对随意方法的进步,无论我们是做故障演练、调用链追踪(QTrace)、流量录像平台(Ares)以及动态扩张日志输出BTrace,都需求1个独具无侵入、实时生效、动态可插拔的字节码增强组件。

Agent的事件模型

网站地图xml地图