当前位置: 首页 > news >正文

滴滴注册网站wordpress简体转繁体

滴滴注册网站,wordpress简体转繁体,今天正式封城,cps推广JavaAgent 技术原理及实战 1、引子2、JavaAgent 简单示例#xff1a;方法开始和结束时打印日志2.1 创建 Agent2.2 编写验证 agent 功能的测试类2.2.1 使用JavaAgent 静态加载方式2.2.2 使用 JavaAgent 动态加载方式 2.3、小结 3、JavaAgent3.1 JavaAgent是什么#xff1f;3.2… JavaAgent 技术原理及实战 1、引子2、JavaAgent 简单示例方法开始和结束时打印日志2.1 创建 Agent2.2 编写验证 agent 功能的测试类2.2.1 使用JavaAgent 静态加载方式2.2.2 使用 JavaAgent 动态加载方式 2.3、小结 3、JavaAgent3.1 JavaAgent是什么3.2 原理解析3.2.1 JVMTI3.2.2 Instrumentation3.2.3 JavaAgent 的加载3.2.3.1 静态加载3.2.3.2 动态加载 4、实战使用JavaAgent实现全链路监控基础版4.1 概述4.2 技术环境4.3、常见问题4.3.1 JavaAgent4.3.2 常用的修改字节码的工具4.3.3 为什么跨线程不能透传traceId如何解决4.3.4 ThreadLocal、InheritableThreadLocal和TransmittableThreadLocal三者区别4.3.5 Spring Boot Starter4.3.6 JavaAgent与Spring AOP和AspectJ之间有什么区别 源码下载 1、引子 线上故障群发来一条用户投诉用户抱怨页面加载时间过长有时甚至超时。这时你首先检查了服务器和数据库但并未发现有问题。你尝试在开发环境中复现问题但一切运行正常。这个问题只在生产环境的特定时段出现常规的Debug方式并不奏效。 此刻的你感到困惑和无助不禁开始怀疑是不是应用程序的某个部分在增加系统的延迟有没有方法可以帮助你追踪代码的运行过程看一看到底哪里出现了问题 此时JavaAgent技术闪耀登场。这项技术可以帮助你深入观察应用程序的运行状态助你洞察问题的根源。那么我们就一起来探索一下JavaAgent技术。 2、JavaAgent 简单示例方法开始和结束时打印日志 2.1 创建 Agent 创建javaagent-demo工程目录结构如下 新建 pom.xml​引入 javassist​ 用来修改目标类的字节码增加自定义代码。通过 maven-assembly-plugin 插件打包自定义的 agent jar。 ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsdparentartifactIdjavaagent-demo/artifactIdgroupIdorg.example/groupIdversion1.0-SNAPSHOT/version/parentmodelVersion4.0.0/modelVersionartifactIdagent-demo/artifactIdpackagingjar/packagingpropertiesproject.build.sourceEncodingUTF-8/project.build.sourceEncodingproject.reporting.outputEncodingUTF-8/project.reporting.outputEncodingmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.target/propertiesdependenciesdependencygroupIdorg.javassist/groupIdartifactIdjavassist/artifactIdversion3.25.0-GA/version/dependency/dependenciesbuildpluginsplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-assembly-plugin/artifactIdversion3.1.1/versionconfigurationdescriptorRefs!--将应用的所有依赖包都打到jar包中。如果依赖的是 jar 包jar 包会被解压开平铺到最终的 uber-jar 里去。输出格式为 jar--descriptorRefjar-with-dependencies/descriptorRef/descriptorRefsarchive!-- 设置manifest配置文件--manifestEntries!--Premain-Class: 代表 Agent 静态加载时会调用的类全路径名。--Premain-Classcom.atu.DemoAgent/Premain-Class!--Agent-Class: 代表 Agent 动态加载时会调用的类全路径名。--Agent-Classcom.atu.DemoAgent/Agent-Class!--Can-Redefine-Classes: 是否可进行类定义。--Can-Redefine-Classestrue/Can-Redefine-Classes!--Can-Retransform-Classes: 是否可进行类转换。--Can-Retransform-Classestrue/Can-Retransform-Classes/manifestEntries/archive/configurationexecutionsexecution!--绑定到package生命周期阶段上--phasepackage/phasegoals!--绑定到package生命周期阶段上--goalsingle/goal/goals/execution/executions/pluginplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-compiler-plugin/artifactIdversion3.1/versionconfigurationsource${maven.compiler.source}/sourcetarget${maven.compiler.target}/target/configuration/plugin/plugins/build/project编写agent核心代码 DemoAgent.java我们使用了premain()​静态加载方式agentmain()​动态加载方式。并用到了Instrumentation​类结合javassist代码生成库进行字节码的修改。 public class DemoAgent {/*** 被转换的类*/private static String TRANSFORM_CLASS com.atu.Test;public static void premain(String agentArgs, Instrumentation inst) {// 在这里可以对应用程序进行字节码转换// 例如添加一个Transformerinst.addTransformer(new MyClassTransformer());}/*** 动态加载。Java agent指定的premain方法会在main方法之前被调用*/public static void agentmain(String args, Instrumentation inst) {System.out.println(agentmain start!);inst.addTransformer(new MyClassTransformer());Class?[] classes inst.getAllLoadedClasses();if (classes ! null) {for (Class? c : classes) {if (c.isInterface() || c.isAnnotation() || c.isArray() || c.isEnum()) {continue;}if (c.getName().equals(TRANSFORM_CLASS)) {try {System.out.println(retransformClasses start, class: c.getName());/** retransformClasses()对JVM已经加载的类重新触发类加载。使用的就是上面注册的Transformer。* retransformClasses()可以修改方法体但是不能变更方法签名、增加和删除方法/类的成员属性*/inst.retransformClasses(c);System.out.println(retransformClasses end, class: c.getName());} catch (UnmodifiableClassException e) {System.out.println(retransformClasses error, class: c.getName() , ex: e);e.printStackTrace();}}}}System.out.println(agentmain end!);} }public class MyClassTransformer implements ClassFileTransformer {/*** 被转换的类*/private static String TRANSFORM_CLASS com.atu.Test;Overridepublic byte[] transform(ClassLoader loader, String className,Class? classBeingRedefined, ProtectionDomain protectionDomain,byte[] classfileBuffer) throws IllegalClassFormatException {// 在这里可以修改字节码// 例如打印类名try {className className.replace(/, .);if (className.equals(TRANSFORM_CLASS)) {final ClassPool classPool ClassPool.getDefault();final CtClass clazz classPool.get(TRANSFORM_CLASS);for (CtMethod method : clazz.getMethods()) {/** Modifier.isNative(methods[i].getModifiers())过滤本地方法,否则会报* javassist.CannotCompileException: no method body at javassist.CtBehavior.addLocalVariable()* 报错原因如下* 来自Stack Overflow网友解答* Native methods cannot be instrumented because they have no bytecodes.* However if native method prefix is supported ( Transformer.isNativeMethodPrefixSupported() )* then you can use Transformer.setNativeMethodPrefix() to wrap a native method call inside a non-native call* which can then be instrumented*/if (Modifier.isNative(method.getModifiers())) {continue;}method.insertBefore(System.out.println(\ clazz.getSimpleName() . method.getName() start.\););method.insertAfter(System.out.println(\ clazz.getSimpleName() . method.getName() end.\);, false);}return clazz.toBytecode();}} catch (Exception e) {e.printStackTrace();}return classfileBuffer;} }编译打包: 2.2 编写验证 agent 功能的测试类 public class Test {public static void main(String[] args) throws InterruptedException {System.out.println(helloworld!);Thread.sleep(20000);} }2.2.1 使用JavaAgent 静态加载方式 在 IDEA 的 Run/Debug Configurations 中点击 Modify options勾选上 add VM options在 VM options 栏增加 -javaagent:全路径\agent-demo-1.0-SNAPSHOT-jar-with-dependencies.jar。 运行 Test.java 的 main 方法可以看到控制台日志 2.2.2 使用 JavaAgent 动态加载方式 动态加载不是通过 -javaagent: 的方式实现而是通过 Attach API 的方式。 编写调用 Attach API 的测试类 public class AttachMain {public static void main(String[] args) throws Exception {// agentmain()方法所在jar包String jar E:\\projects\\mycode\\javaagent-demo\\agent-demo\\target\\agent-demo-1.0-SNAPSHOT-jar-with-dependencies.jar;for (VirtualMachineDescriptor virtualMachineDescriptor : VirtualMachine.list()) {// 针对指定名称的JVM实例if (virtualMachineDescriptor.displayName().equals(com.atu.Test)) {System.out.println(将对该进程的vm进行增强com.atu.Test的vm进程, pid virtualMachineDescriptor.id());// attach到新JVMVirtualMachine vm VirtualMachine.attach(virtualMachineDescriptor);// 加载agentmain所在的jar包vm.loadAgent(jar);// detachvm.detach();}}} }先直接运行 com.atu.Test#main​注意不用加 -javaagent: 启动参数。 约 5 秒后再运行 com.atu.AttachMain#main可以看到 com.atu.AttachMain#main 打印的日志 将对该进程的vm进行增强com.atu.Test的vm进程, pid15544之后可以看到 com.atu.Test#main打印的日志中多了记录方法运行开始和结束的内容。 helloworld! agentmain start! retransformClasses start, class: com.atu.Test retransformClasses end, class: com.atu.Test agentmain end!2.3、小结 可以看到静态加载或动态加载相同的 agent都能实现了记录记录方法运行开始和结束日志的功能。 下面进入正题什么是JavaAgent 3、JavaAgent 3.1 JavaAgent是什么 JavaAgent 是一种特殊的类它提供了一种能力使得我们可以在Java程序运行期间对加载到JVM中的类进行字节码层面的修改和增强。 简单来说JavaAgent能够“拦截”类加载过程在类被使用前“改变”它的行为。 JavaAgent本质上可以理解为一个插件该插件就是一个精心提供的jar包这个jar包通过JVMTIJVM Tool Interface完成加载最终借助JPLISAgentJava Programming Language Instrumentation Services Agent完成对目标代码的修改。 JavaaAgent通常用在如性能监控Profiler、代码热替换、动态追踪等领域。一些知名的工具比如JRebel代码热替换、SkyWalking性能监控就是基于JavaAgent 实现的。 3.2 原理解析 3.2.1 JVMTI JVMTI​ JVM Tool Interface是 Java 虚拟机对外提供的 Native 编程接口通过 JVMTI 外部进程可以获取到运行时 JVM 的诸多信息比如线程、GC 等。 JVMTI是基于事件驱动的JVM每执行一定的逻辑就会触发一些事件的回调接口通过这些回调接口用户可以自行扩展实现自己的逻辑。 JVMTI 是一套 Native 接口在 Java SE 5 之前要实现一个 Agent 只能通过编写 Native 代码来实现。从 Java SE 5 开始可以使用 Java 的Instrumentation 接口java.lang.instrument来编写 Agent。无论是通过 Native 的方式还是通过 Java Instrumentation 接口的方式来编写 Agent它们的工作都是借助 JVMTI 来进行完成。 Instumentation API 可以支持 Java 语言实现 agent 功能但是 JVMTI 功能比 Instumentation API 更强大。 3.2.2 Instrumentation Instrumentation 是 Java 提供的 JVM 接口该接口提供了一系列查看和操作 Java 类定义的方法例如修改类的字节码、向 classLoader 的 classpath 下加入 jar 文件等。使得开发者可以通过 Java 语言来操作和监控 JVM 内部的一些状态进而实现 Java 程序的监控分析甚至实现一些特殊功能如 AOP、热部署。 Instrumentation接口中最常用的方法是 addTransformer(ClassFileTransformer transformer)​这个方法可以在类加载时做拦截对输入的类的字节码进行修改其参数是一个ClassFileTransformer接口 public interface ClassFileTransformer {/*** 传入参数表示一个即将被加载的类包括了classloaderclassname和字节码byte[]* 返回值为需要被修改后的字节码byte[]*/byte[]transform( ClassLoader loader,String className,Class? classBeingRedefined,ProtectionDomain protectionDomain,byte[] classfileBuffer)throws IllegalClassFormatException; }3.2.3 JavaAgent 的加载 JavaAgent 支持静态加载和动态加载。 3.2.3.1 静态加载 静态加载即 JVM 启动时加载对应的是 premain()​ 方法。通过 vm 启动参数-javaagent 将 agent jar 挂载到目标 JVM 程序随目标 JVM 程序一起启动。 加载 JavaAgentJavaAgent 中的 class 通常是由 system calss loader(默认AppClassLoader) 加载。调用 premain方法 JVM 加载完成 JavaAgent 的入口类之后会调用其 premain 方法使用 Instrumentation API通过 premain 方法JavaAgent 得到了 Instrumentation 的实例。JavaAgent 可以使用这个 Instrumentation 实例来注册一个或多个 ClassFileTransformer或者进行其它需要的操作。这是 JavaAgent 真正开始发挥作用的地方。Instrumentation API 调用 JVMTI JVMTI 是 JNI (Java Native Interface) 的一部分提供了丰富的接口供 Instrumentation API 和 JVM 进行交互。执行 ClassFileTransformer当 Instrumentation 需要载入类时如果 Java Agent 对该类注入了 ClassFileTransformerJVMTI 会回调 Instrumentation API然后调用对应的 ClassFileTransformer.transform 方法。在 transform 方法中JavaAgent 可以修改类的字节码。premain() 方法会调用 Instrumentation API然后 Instrumentation API 调用 JVMTI(JVMTI 的内容将在后面补充)在需要加载的类需要被加载时会回调 JVMTI然后回调 Instrumentation API触发 ClassFileTransformer.transform()最终修改 class 的字节码。加载主类并启动应用 所有的 JavaAgent 加载和初始化完成后JVM 会准备开始加载主类并执行其 main 方法。 ClassFileTransformer.transform是 Java Instrumentation API 中的核心方法它的作用是在类文件被 JVM 加载之前对其进行字节码级别的转换和修改。 3.2.3.2 动态加载 JVM运行时加载可以在 main 函数开始运行之后再运行。通过Attach API​动态地加载 JavaAgent对应的是 agentmain() 方法。 基本流程 创建一个 VirtualMachine 实例首先使用 VirtualMachineDescriptor 获取要附加的Java虚拟机的描述并通过 VirtualMachine.attach() 方法连接到该JVM。加载JavaAgent在连接到 JVM 后使用 VirtualMachine.loadAgent() 方法加载JavaAgent的jar包。修改字节码断开连接改完成后通过 VirtualMachine.detach() 方法断开与JVM的连接。所有的更改都会在目标JVM中保存不会影响到运行Agent的JVM。 4、实战使用JavaAgent实现全链路监控基础版 4.1 概述 利用 javassist 对 Log 框架进行切面增强。利用 Spring 的拦截器技术实现了 Web 请求的 traceId 初始赋值。通过整合 dubbo SPI结合 dubbox 调用拦截器实现 traceId 的拦截及赋值。引入TransmittableThreadLocal 解决父子线程上下文传递的问题。 4.2 技术环境 Javassist 3.25.0-GADubbox 2.6.5Spring Boot 2.0.5.RELEASETransmittable 2.12.2 4.3、常见问题 4.3.1 JavaAgent JavaAgent 是一种特殊的 Java 程序它利用 Java 的 Instrumentation 机制在运行时改变或分析其他Java 程序的行为并对其进行监控、调试、或性能优化等操作。 有了 JavaAgent 技术可以在字节码这个层面对类和方法进行修改可以把 JavaAgent 理解成一种代码注入的方式或者可以说 JavaAgent 就是 JVM 层面的代理程序。 4.3.2 常用的修改字节码的工具 ASMJavassistByteBuddy 4.3.3 为什么跨线程不能透传traceId如何解决 我们常用的日志框架比如 LogbackLog4j 等通过使用 MDCMapped Diagnostic Context映射调试上下文在多线程环境下记录日志。 我们的请求链路ID也是借助 MDC 实现传递的。 MDC 是以线程为基础的存储结构每个线程都有其自己的一份独立的 MDC 数据。这是通过底层的 ThreadLocal 实现的ThreadLocal 为每个线程提供了一个独立的数据副本每个线程都只能看到及修改自己的 ThreadLocal 副本数据而看不到其他线程的数据这样可以有效避免数据之间的相互影响。 那么当异步方法切换线程的时候就会出现上下文信息传递丢失的问题。从而导致 TraceID 丢失的问题。 我们需要在父线程中手动获取并传递 MDC 数据到子线程解决数据跨线程传递的问题。 4.3.4 ThreadLocal、InheritableThreadLocal和TransmittableThreadLocal三者区别 1)、ThreadLocal ThreadLocal主要是为每个ThreadLocal对象创建一个ThreadLocalMap来保存对象和线程中的值的映射关系。 当创建一个ThreadLocal对象时会调用get()或set()方法在当前线程的中查找这个ThreadLocal对象对应的Entry对象如果存在就获取或设置Entry中的值否则在ThreadLocalMap中创建一个新的Entry对象。 ThreadLocal类的实例被多个线程共享每个线程都拥有自己的ThreadLocalMap对象存储着自己线程中的所有ThreadLocal对象的键值对。 ThreadLocal的实现比较简单但需要注意的是如果使用不当可能会出现内存泄漏问题因为ThreadLocalMap中的Entry对象并不会自动删除。 2)、InheritableThreadLocal InheritableThreadLocal的实现方式和ThreadLocal类似但不同之处在 Thread 类的 init() 方法中当创建新的线程时会调用 inheritThreadLocals(parentThread) 方法这个方法就是将父线程的 InheritableThreadLocalMap 注意这里并不是 ThreadLocalMap复制一份到子线程中。 局限性InheritableThreadLocal 支持子线程访问在父线程的核心思想是在创建线程的时候将父线程中的本地变量值复制到子线程即复制的时机为创建子线程时。 线程池能够复用线程减少线程的频繁创建与销毁如果使用 InheritableThreadLocal 那么线程池中的线程拷贝的数据来自于第一个提交任务的外部线程即后面的外部线程向线程池中提交任务时子线程访问的本地变量都来源于第一个外部线程造成线程本地变量混乱。 3)、TransmittableThreadLocal TransmittableThreadLocal 是阿里巴巴开源的专门解决 InheritableThreadLocal 的局限性实现线程本地变量在线程池的执行过程中能正常的访问父线程设置的线程变量。 TTL 的设计理念在于每次线程执行任务时都会备份当前线程的 TTL 值然后从提交任务的线程那里拷贝一份新的 TTL 值到当前线程 任务执行完成后再将备份的 TTL 值恢复回当前线程。 具体流程如下 任务提交到线程池时首先会将提交任务的线程父线程的 TTL 值拷贝一份然后作为本次任务执行的上下文。线程获取到任务执行时先将线程原先的 TTL 值进行备份然后将第一步拷贝的 TTL 值设置到线程中。任务执行完成后线程将自身的 TTL 值设置回第二步备份的值。 通过这种方式TTL 成功解决了在使用 InheritableThreadLocal 时线程池中由于线程复用导致的问题确保了每次任务执行时线程内的 TTL 值都是我们期望的那个值。 4.3.5 Spring Boot Starter Spring Boot Starter 是 Spring Boot 框架提供的一种特性它是一种提供依赖项的方式可以帮助开发人员快速集成各种第三方库和框架。 Spring Boot Starter 的目的是简化 Spring 应用程序的依赖管理将一组相关的依赖项打包在一起并提供一个依赖项描述文件使开发人员可以快速集成。 Spring Boot Starter 本质上是一个包含了必要依赖和自动配置类的 Maven 依赖是一系列依赖集合它能够自动配置应用程序的运行环境并提供默认的配置选项让开发人员可以快速开始开发。 举个例子如果在 Spring Boot 项目中使用 Spring MVC需要引入多个与Spring MVC相关的依赖包括 spring-webmvc、spring-web等这时候如果使用 spring-boot-starter-web 这个starter只需要添加一个依赖就可以了它会包含使用 Spring MVC 所需要的所有依赖。 4.3.6 JavaAgent与Spring AOP和AspectJ之间有什么区别 1)、JavaAgent Java Agent 是 Java 5 引入的一种机制它能够通过预处理Pre-processing和类转换Class Transformation的方式修改已有的字节码。 Java Agent 通常在 JVM 启动或者类加载时进行操作。每当一个类被 JVM 加载都会调用 Java Agent 的代理方法以便进行类字节码的转换。 因此Java Agent 更底层使用复杂但功能十分强大。 2)、AspectJ AspectJ 是最早的切面编程框架之一并且它提供了非常强大的切面编程能力。 AspectJ 通过类似于 Java 语言的 AspectJ 语言来书写切面并且提供了一个 AspectJ 编译器将 AspectJ 代码编译成可以运行的字节码。 AspectJ 支持更多更细粒度的切入点如方法调用实例创建等能够在运行时进行热替换和精细控制其功能强大但使用和学习成本较高。 3)、Spring AOP Spring AOP 是 Spring 框架提供的切面编程实现。 它主要利用 Java 的动态代理机制以及 CGLIB 库来在运行时动态地创建对象的代理。 与 AspectJ 相比Spring AOP 更轻量级且简单但其切入点种类有限主要支持方法执行切点不能做到类似 AspectJ 的构造函数或属性切入。 使用便捷适用于一般的日志、事务等场景对于复杂的切点和切面控制较为局限。 源码下载 源码 【文章参考】 【JVM】Java agent超详细知识梳理
http://www.pierceye.com/news/645691/

相关文章:

  • 南昌商城网站建设央企网站群建设中标公告
  • 湖北建设监理协会网站网页游戏排行榜开服时间
  • 中国空间站图片竞价账户托管公司哪家好
  • 湖南省金力电力建设有限公司 网站广州市建设局官方网站
  • 比价网站源码免费域名领取
  • 温州做网站费用怎么推广自己的偏方
  • 鞍山建站wordpress侧边浮动
  • 江西网站优化临海外发加工网
  • 机械做网站好处无锡网络推广服务
  • 电信网站备案管理系统外贸网站推广的方法
  • 什么网站上公司的评价最客观需要做网站建设和推广的行业
  • 百度人工服务24小时电话四川seo技术培训
  • 邢台网站建设服务商教育网站制作软件
  • 罗湖区住房和建设局网站官网建设香帅摩托车官网
  • 网站建设一条东莞手机微信网站
  • 网站界面设计修改要多少钱wordpress博客转出
  • 17网站一起做网店代发流程建立一个网站需要人员
  • 如何添加网站关键词天津建设工程信息网官罿
  • 医疗网站的建设设计要注意什么做网站赚钱吗 怎么赚
  • 创造有价值的网站建设银行网站上改手机
  • 商城网站租服务器安全不怎么做和美团一样的网站
  • 网站建设 sql 模版做网站的工具 论坛
  • 万网虚拟主机两个网站权重域名做网站有用么
  • 门户网站是指六盘水做网站
  • 自助建站系统免费加盟设计用哪些网站有哪些
  • 太原制作公司网站无锡网站优化公司
  • html5 wap网站模板动画广州网站建设 信科公司
  • 西安门户网站开发wordpress如何在文章底部添加目录
  • 设计婚纱网站宁波网站优化服务
  • 建设电子商务网站的花费那些公司做网站比较厉害