首页 > 编程语言 >解析Sermant热插拔能力:服务运行时动态挂载JavaAgent和插件

解析Sermant热插拔能力:服务运行时动态挂载JavaAgent和插件

时间:2024-02-08 09:33:05浏览次数:33  
标签:插件 字节 热插拔 JavaAgent 增强 Sermant 加载

本文分享自华为云社区《服务运行时动态挂载JavaAgent和插件——Sermant热插拔能力解析》,作者:华为云高级软件工程师 栾文飞

一、概述

Sermant是基于Java字节码增强技术的无代理服务网格,其利用Java字节码增强技术,为宿主应用程序提供服务治理功能,以解决大规模微服务场景中的服务治理问题,通过Java字节码增强技术,可以非侵入的提供服务治理能力。在以往版本中,Sermant通过配置-javaagent指令在微服务启动时接入服务治理能力,当需要接入及卸载Sermant时都需要通过重新启动微服务来完成。但从1.2.0版本开始,Sermant实现了在服务不停机状态下进行安装和卸载的能力,为服务治理能力带来全新接入体验。本文将会对这种动态接入的机制,从技术基础到Sermant设计进行一次深入分析。

二、JavaAgent加载方式

首先介绍一下JavaAgent的不同接入方式,这是Sermant实现动态接入能力的技术基础。Java 中Instrumentation API 提供了一种修改字节码的机制,利用该API,可以通过修改字节码的方式来改变程序的行为,而不用触及程序的源码。JavaAgent为Instrumentation API的客户端,通过JavaAgent可以调用API进行字节码的操作,其提供了两种加载方式给开发者重载:

  • 静态加载:利用premain,在应用程序启动时加载 JavaAgent称为静态加载,静态加载会在启动时在执行任何代码之前修改字节码。

静态加载时,字节码增强是在类加载时发生的,当Java程序启动时,类加载过程中所有被加载的类都会经过JavaAgent所定义的类文件转换器的处理。

  • 动态加载:利用agentmain通过Java Attach API将JavaAgent加载到已运行的JVM中,动态加载可以通过字节码重转换的方式在运行时修改字节码。

 

动态加载时,和静态加载不同的是,此时JVM已在运行,目标类已被加载,就不能像静态加载时一样触发字节码增强过程,在使用动态加载的过程中,往.往会通过Instrumentation API来触发目标类(当然也可以指定所有已被加载的类)的重转换过程,在重转换过程中就会触发到Agent构建的类文件转换器,从而完成字节码增强过程。

动态加载方式为JavaAgent提供了在JVM运行时接入的能力,但通过类重转换来触发字节码增强相对于在类加载时增强有一定的局限性,例如不能在增强时修改类的继承关系,不能为类添加静态代码块,不能增强内存中和资源文件中字节码不一致的类等,这些也是在使用动态加载和多JavaAgent场景中常见的问题,综上,两种加载方式各有利弊,可以在使用时按照业务场景选择。

三、Sermant热插拔能力关键问题剖析

在了解技术基础后,我们能轻易的想到,理论上基于JavaAgent的动态加载方式,只需要在使用Sermant时,将通过premain方式启动改为通过agentmain方式启动,就可以将微服务治理能力动态的接入到微服务中,做到微服务零侵入、微服务不停机的状态下接入服务治理能力,但通往前方的路上总是充满了障碍:

3.1 如何保证动态安装过程中重转换可顺利执行?

这个问题的出现,根源在于JavaAgent通过agentmain方式加载到已运行的JVM中时,不同于静态加载,会在类初次被加载时完成字节码的转换,动态加载时一些需要被字节码增强类已经完成了类加载过程,这时候需要使用Instrumentation提供的类重转换(retransform classes)能力来修改字节码,在Instrumentation的Javadoc中关于这个能力有这样一段描述:

“The retransformation must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance.(重转换过程中,我们不能新增、删除或者重命名字段和方法,不能更改方法的签名,不能更改类的继承。)”

从中可以看出,在引入动态加载能力前,优先要保证字节码增强时,不可以有上述内容中所描述的限制操作。

不过Sermant不太需要担心这个问题,因为这种限制不仅仅在动态加载时会触发,在多个JavaAgent同时使用时也可能会触发,可以参考Sermant团队的另一篇文章:《记一次多个JavaAgent同时使用的类增强冲突问题及分析》。为了保证在多Agent场景下的兼容性,Sermant的字节码增强模板严格遵循Instrumentation API的限制,因此Sermant在兼容性上的不断改进过程中无心插柳,帮助动态加载能力铺平了路。

3.2 如何保证在服务治理插件安装和卸载时不互相影响?

Sermant的设计中,通过字节码增强引入的服务治理能力,是通过在目标方法上添加服务治理功能切面来完成的,每一个服务治理插件,通过一系列切面的配合来达成最终的服务治理效果。不同的服务治理功能,可能会对同一个目标方法进行处理。但并不会对同一个方法进行多次字节码增强,而是通过一次字节码增强织入调度切面(onMethodEnter、onMethodExit等),通过该切面对相关的服务治理能力(通过拦截器实现,每一个切面会对应一个拦截器的列表)进行调度:

对于服务治理能力的调度逻辑我们在另一篇文章《开发者能力机制解析,玩转Sermant开发》有讲过,本篇不再赘述。

基于框架的基本设计,就需要考虑两个问题,当插件在动态安装时,如何保证不重复字节码增强?当插件卸载时,如何保证不会导致有相同目标方法的插件失效。

  • 安装时如何保证不重复执行字节码增强?

在字节码增强开发过程中,类文件转换器(ClassFileTransformer)是一定会接触到的概念,开发者需要基于该转换器来进行字节码的处理。在大多数的字节码增强框架中,都会对其进行封装,用于降低字节码处理的难度。Sermant基于ByteBuddy提供的类文件转换器实现了一种可重入的类转换器,在插件动态安装时,虽然目标方法已经被已安装的插件增强过了,但此时还是会触发类文件转换(因为动态安装插件的过程是独立的),当触发类文件转换时,所有相关的类文件转换器都会被唤醒,再次触发类文件转换过程。每次可重入类转换器被唤醒时,将发生以下行为:

在Sermant中维护了一个针对目标方法的字节码增强锁(AdviceKey锁),即针对每一个目标方法,维护了1个信号量当做锁,用于让各类文件转换器来检查目标方法的字节码增强状态,当目标方法对应的类被类转换时,就会触发Sermant所提供的类文件转换器,此时类文件转换器将尝试获取针对目标方法的信号量,如果能获取信号量,则执行对目标方法的字节码增强,如果不能获取,则不执行字节码增强。

基于字节码增强锁,在转换器触发时,主要有两条路径可以走,类文件转换器会通过目标方法的AdviceKey(类名+方法hash+类加载器组成的一个唯一表示,用于表示字节码增强的目标)来检查其所关联的锁,判断当前目标方法是否已被Sermant进行过字节码增强(织入拦截器调度的切面):

  1. 能获取锁,说明未被增强:则当前文件转换器获取当前AdviceKey所关联的锁,将其获取的锁通过其对应的插件来维护,并且执行字节码增强,将服务治理所需的拦截器放入该AdviceKey所对应的拦截器列表;
  2. 不能获取锁,说明已被增强:则只将拦截器放入该AdviceKey对应的拦截器列表中,不执行字节码增强。

通过上述机制,就可以保证Sermant在安装不同服务治理插件时,不会进行重复的字节码增强,避免无端的性能和资源损耗。

  • 卸载时如何保证不会导致其他插件失效?

当插件需要卸载时,会再次触发相关目标类的重转换,与安装时不同的是,这次需要被卸载的插件释放自身已经持有的AdviceKey锁。释放锁后,触发目标类重转换时,目标类所对应的各个插件的类文件转换器将会再次触发和安装时相同的流程:

在这个过程中,未被卸载的插件所提供的对目标类的类文件转换器,会在目标类重转换时,再次触发,并且只会经历获取锁和字节码增强的过程。这样就保证,如果还有插件需要对该目标方法进行字节码增强时,可以获得目标方法所对应的锁,不会因为目标方法的交集而导致其他插件能力失效。

四、总结

本篇文章对Sermant的热插拔能力的核心机制进行了解析,希望可以为开发者及使用者在开发或使用相关能力时带来更多的灵感和便利。更多的热插拔能力介绍可以参考官网相关文档,Sermant Agent使用手册,后续我们也会针对热插拔适用的场景进行进一步分享,敬请期待。

Sermant作为专注于服务治理领域的字节码增强框架,致力于提供高性能、可扩展、易接入、功能丰富的服务治理体验,并会在每个版本中做好性能、功能、体验的看护,广泛欢迎大家的加入。

 

点击关注,第一时间了解华为云新鲜技术~

 

标签:插件,字节,热插拔,JavaAgent,增强,Sermant,加载
From: https://www.cnblogs.com/huaweiyun/p/18011591

相关文章

  • 图片延迟加载插件lazyload使用
    图片懒加载,又称图片延时加载、惰性加载,即在用户需要使用图片的时候才加载。这种技术的主要好处包括:优化页面加载速度:在页面被请求时,只加载可视区域的图片,其它部分的图片则不加载,只有当这些图片出现在可视区域时才会动态加载。这样可以减少首次加载时需要加载的内容量,显著提高页面加......
  • x64dbg 防止丢失数据库的插件 DbGit
    我跟了一晚上,说丢就丢了,真的无语~找到个使用git的方式备份数据库的插件DbGit 试了下,使用很简单!在手动保存或退出x64dbg时会自动执行 git add -A git commit -m命令。数据库似乎必须得在软件目录下的db目录才行,在被调试程序目录,似乎不行! 从此不会丢数据了,也算......
  • SpringBoot的maven插件生成可以直接启动的jar
    简单使用<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration&g......
  • Android Studio 中使用uiautomatorviewer插件
    在Android设备中,各种的组件如何分布以及控件的具体信息如何查看呢。在AndroidStudio中有一个可以查看的插件,本篇文章就介绍这个插件的使用- uiautomatorviewer.bat1.综述  AndroidStudio是Google官方提供的一款用于开发Android应用程序的集成开发环境(IDE),它基于IntelliJIDEA......
  • MyBatis使用分页插件
    MyBatis使用分页插件PageHelper是国内非常优秀的开源mybatis分页插件,它支持基本主流与常用的数据库,例如:mysql、Oracle、DB2等。PageHelper在GitHub的项目地址:https://github.com/pagehelper/Mybatis-PageHelper分页插件的原理(1)在MyBatis执行SQL语句之前,拦截器可以获取SQL......
  • 利用Jquery Lazyload JS插件实现网页图片延迟加载
    JqueryLazyload是一款网页图片延迟加载JS插件,本文介绍该JS的使用方法。最新的jquerylazyload可以单独使用(即不依赖jquery),本文介绍的是依赖jquery的使用及配置方法。Github项目地址:https://github.com/tuupola/lazyload本文使用的是1.9.7版本(实际与1.9.6版本一致)首先需要引用......
  • 第22天:安全开发-PHP应用&留言板功能&超全局变量&数据库操作&第三方插件引用
    #数据库操作-mysqli函数&增删改查PHP函数:连接,选择,执行,结果,关闭等参考:https://www.runoob.com/php/php-ref-mysqli.html常用:mysqli_connect()打开一个到MySQL的新的连接。mysqli_select_db()更改连接的默认数据库。mysqli_query()执行某个针对数据库的查询。mysqli_fetch......
  • pinterest插件集中移动图片到指定文件夹
    importosimportshutildefis_image_file(filename):"""检查文件是否为图片文件。"""image_extensions=['.jpg','.jpeg','.png','.gif','.bmp','.tiff']returnany......
  • 6款超好用的IDEA插件,开发必备!
    今天给大家介绍几款开发必备的IDEA插件:JRebel热部署插件,让你在修改完代码后,不用再重新启动,很实用!但是,不是免费的,需要大家继续发挥下自己的聪明才智才能happy的使用 JsonParser厌倦了打开浏览器格式化和验证JSON?为什么不安装JSON解析器并在IDE中使用离线支持呢?JSON解析器......
  • PbootCMS采集发布插件-免费下载
    推荐一款可以自动采集文章数据,并发布到Pboot网站的PbootCMS采集发布插件,支持对接简数采集器,火车头采集器,八爪鱼采集器,后羿采集器等大多数网页采集软件。1. 下载并安装PbootCMS采集发布插件1-1)PbootCMS采集发布插件免费下载地址:PbootCMS采集发布接口下载1-2)解压PbootCMS采集发......