0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

王程 来源:jf_75796907 作者:jf_75796907 2024-02-18 10:09 次阅读

一、概述

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 称为静态加载,静态加载会在启动时在执行任何代码之前修改字节码。

wKgZomXPLDiAOeKFAACU6YF6H-4790.pngwKgZomXPK9uAWf__AACU6YF6H-4998.png

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

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

wKgaomXPLDmAOjbwAACZQB7Vbyo997.pngwKgZomXPK--AcDYZAACZQB7Vbyo681.png

动态加载时,和静态加载不同的是,此时 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 等),通过该切面对相关的服务治理能力(通过拦截器实现,每一个切面会对应一个拦截器的列表)进行调度:

wKgZomXPLDmALI3hAACenH9Ro5I964.pngwKgaomXPK_6AAENeAACenH9Ro5I795.png

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

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

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

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

wKgaomXPLDqAPjSxAAJmM8E6Ha0534.pngwKgZomXPLBGARfotAAJmM8E6Ha0706.png

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

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

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

2.不能获取锁,说明已被增强:则只将拦截器放入该 AdviceKey 对应的拦截器列表中,不执行字节码增强。
通过上述机制,就可以保证 Sermant 在安装不同服务治理插件时,不会进行重复的字节码增强,避免无端的性能和资源损耗。

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

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

wKgZomXPLDuAJYdJAAJ4ZuzkUMQ275.pngwKgaomXPLCSABj97AAJ4ZuzkUMQ731.png

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

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 转换器
    +关注

    关注

    27

    文章

    8682

    浏览量

    147067
  • 热插拔
    +关注

    关注

    2

    文章

    221

    浏览量

    37310
  • JAVA
    +关注

    关注

    19

    文章

    2965

    浏览量

    104691
收藏 人收藏

    评论

    相关推荐

    NI发布全新PXI机箱,全面提高系统正常运行时

      在NI PXIe-1066DC机全新机箱中,NI为PXI平台系列增加了冗余、热插拔和前端接入功能,显著增加了系统的正常运行时
    发表于 05-28 09:08 2159次阅读

    如何缩短Vivado的运行时

    在Vivado Implementation阶段,有时是有必要分析一下什么原因导致运行时间(runtime)过长,从而找到一些方法来缩短运行时间。
    的头像 发表于 05-29 14:37 1.4w次阅读
    如何缩短Vivado的<b class='flag-5'>运行时</b>间

    基于PCIe-Native机制的热插拔

    热插拔即带电插拔,在虚拟化场景下,热插拔就是在虚拟机运行过程中对磁盘网卡等设备进行动态调整。
    的头像 发表于 09-06 10:32 4237次阅读

    如何检查Linux服务器的运行时

    Linux 中的 uptime 用于查看系统启动后的运行时间。它是一个比较简单的 Linux 命令,可以不带参数直接运行
    发表于 11-25 15:25 1.5w次阅读
    如何检查Linux<b class='flag-5'>服务</b>器的<b class='flag-5'>运行时</b>间

    热插拔是什么?热插拔有哪些特点?

    恢复能力、扩展性和灵活性等,例如一些面向高端应用的磁盘镜像系统都可以提供磁盘的热插拔功能。具体用学术的说法就是:热替换(Hot replacement)、热添加(hot expansion)和热升级
    发表于 12-13 10:53

    即插即用和热插拔的区别

    闭系统,不切断电源的情况下取出和更换损坏的硬盘、电源或板卡等部件,从而提高了系统对灾难的及时恢复能力、扩展性和灵活性等,例如一些面向高端应用的磁盘镜像系统都可以提供磁盘的热插拔功能。 具体用学术的说法
    发表于 10-23 10:26

    通过测试的2V输出60A热插拔控制器完整设计

    描述此电路为热插拔设计,使用具有功率限制功能的 TPS2490 热插拔控制器和两个 30V CSD17570Q5B NexFET。此系统可用于安全服务器中,允许在系统正常运行时插入线路
    发表于 11-16 16:50

    采用1.4 代NexFET的12V输出60A热插拔控制器参考设计

    描述此电路为热插拔设计,使用具有功率限制功能的 TPS2490 热插拔控制器和两个 30V CSD17570Q5B NexFET。此系统可用于安全服务器中,允许在系统正常运行时插入线路
    发表于 09-26 07:26

    紫金桥组态软件新的功能_运行时组态

    运行时组态是组态软件新近提出的新的概念。运行时组态是在运行环境下对已有工程进行修改,添加新的功能。它不同于在线组态,在线组态是在工程运行的同时,进入组态环境,在组态环境中对工程进行修改
    发表于 10-13 16:17 2次下载
    紫金桥组态软件新的功能_<b class='flag-5'>运行时</b>组态

    Go运行时:4年之后

    自 2018 年以来,Go GC,以及更广泛的 Go 运行时,一直在稳步改进。近日,Go 社区总结了 4 年来 Go 运行时的一些重要变化。
    的头像 发表于 11-30 16:21 820次阅读

    ch32v307记录程序运行时

    ch32v307记录程序运行时间 在程序开发中,很重要的一项任务就是对程序的运行时间进行评估。对于大型的程序系统来说,它们通常需要处理大量的数据或进行复杂的计算操作。因此,如果程序的运行时间过长
    的头像 发表于 08-22 15:53 899次阅读

    如何保证它们容器运行时的安全?

    紧密耦合的容器运行时继承了主机操作系统的安全态势和攻击面。运行时或主机内核中的任何漏洞及其利用都会成为攻击者的潜在切入点。
    的头像 发表于 11-03 15:24 665次阅读

    热插拔和非热插拔的区别

    热插拔和非热插拔的区别  热插拔和非热插拔是指电子设备或组件在工作状态下是否可以进行插拔操作的一种分类。
    的头像 发表于 12-28 10:01 2953次阅读

    键盘热插拔和非热插拔的区别

    键盘热插拔和非热插拔的区别 键盘是计算机外设设备之一,热插拔是指在计算机运行中插入或拔出设备而无需重启计算机,非热插拔则需要重启计算机才能生
    的头像 发表于 02-02 17:34 1w次阅读

    热插拔是什么意思

    于计算机、服务器、存储设备以及各类电子设备的连接管理中,极大地提高了系统的可靠性、灵活性、可维护性和灾难恢复能力。以下是对热插拔技术的详细解析
    的头像 发表于 10-29 17:49 846次阅读