开发者网络

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 92|回复: 0

“开发过程规范”的个人看法及用JavaAgent实现的规范监测工具 ...

[复制链接]

1

主题

1

帖子

3

积分

新手上路

Rank: 1

积分
3
发表于 2023-3-25 19:41:55 | 显示全部楼层 |阅读模式
概述

对于规范,大部分开发人员对待其时常会有一种矛盾心态;
一方面规范对于软件开发来说是非常重要的,可以保证软件的质量、可维护性和可扩展性。
另一方面又认为规范有时成为了阻碍生产效率的绊脚石,对开发效率产生一定的影响。
主要原因我认为一个重要的因素是规范的不完善,或者说规范配套的工具不够简洁,需要用户参与的过程太多。
解决这个矛盾的关键在于寻找一种平衡点,既可以保证规范的执行,又不会对开发效率造成太大的影响。
归结下来,规范应该是必须的,但必须考虑到执行的效率和工具的简洁性。而就规范来讲,首先是内容(规范本身),其次是执行(工具的使用及结果的处理)。
内容方面,我们内部也不断的完善各项开发规范,包括数据库设计与应用、代码管理、代码开发、日志打印、git分支管理规范。。。行业内有的规范就也会借鉴使用业内标准规范,暂时没有的就先定义符合自身使用的规范。
而对于规范的执行,除了接入行业内已有的工具,如接入自动构建、自动发布等,Sonar代码监测、代码安全检测等。
针对部分规范,业内没有很好的规范和工具,我们也制定了相应的符合自身使用的规范,如数据库设计与应用规范、日志打印规范、GIT分支管理规范等,并且本次着重要讲的***规范监测工具***正是结合上述的规范而开发的,对行业内暂时没有的规范和工具起到查漏补缺锦上添花的作用。
*通常来讲,起名字是一件很难的事情,IT人员对运营一贯的持随意的态度,为了说明干了件还可以的事,还是起个名字吧,叫“夜猫”?其实是之前听过的一首欢快的歌曲名。*
目标

这里我们想要实现一个对规范使用情况监测的工具,对已制定规范的使用情况进行监测,并将监测结果进行收集,以达到对规范使用情况的了解。
规范监测工具包含了数据库设计和使用规范、日志打印规范、Redis的Key使用规范等的检测。
后续根据需要支持适当的进行扩充,如我们一直缺失的日志链路打印、无侵入分布式链路追踪、限流降级等等。
当然,我认为,工具只是手段,最终目标是实现对规范的完善与指导,但在普遍意识未形成前,需要必要的手段对规范的执行提供监测以及依据。
技术选型

通过JavaAgent实现对应用的动态代理,采用VirtualMachine技术实现运行时代码织入,并将代理attach到目标应用进程。
JavaAgent

Agent技术本身是一种开发思维模式,或者说是代理模式,不止Java有,其他语言比如python、go也都有。它就是提供一个外层入口,让你可以对代码做无侵入的动态监控、调用、修改等操作(这其实就是AOP,切面技术)。
JavaAgent是在instrumentation的基础上实现的(instrumentation又基于JVMTI,JVMTI是JVM提供操作native方法的工具,Instrument就是提供给你操纵JVMTI的Java API,详情见[[Instrumentation API]](java.lang.instrument (Java 2 Platform SE 5.0))(Provides services that allow Java programming language agents to instrument programs running on the JVM.))拦截运行的应用程序并修改字节码的技术。其本质实际是在Classloader读取字节码后转化为Class之前,对字节码进行修改,从而实现定制的功能的技术。
JavaAgent分为 「静态Agent」和「动态Agent」两种
静态Agent,需要在目标应用程序启动时,将Agent路径添加进JVM启动参数 -javaagent,也就是需要通过修改启动项的方式让Agent生效。
其原理是instrumentation预先设定了一个premain方法,会先于目标应用程序的main方法调用,premain调用时就可以使用instrument注册类的
Transformer去修改和拦截Class实现更新或创建Class的字节码。修改或创建的Class就是包含你想要实现的功能的Class。
简单讲就是,premain 方 法 用 于 在 启 动 时 , 在 类 加 载 前, 定 义 类 的TransFormer(转化器),通过Transformer在类加载的时候更新对应的类的字节码。


如果**不想修改启动项,则需要动态Agent**。
动态Agent采用**attach方案**,attach是sun公司专门提供为了向目标应用程序附着agent的API,大概过程是先通过VirtualMachine技术获取现有进程的虚拟机,并将Agent附着在指定进程的虚拟机上。
并且,1.6后Instrumentation提供了agentmain方法(本文提到的工具采用的是这种方式),可以让Instrumentation在目标应用程序已经都被加载之后再生效。两者结合就完成了对目标应用程序的动态修改(对字节码的修改和加载也是通过Instrumentation的Transfer),实现“插桩”的效果。
简单讲就是,agentmain方法用于在运行时进行类的字节码的修改,步骤分为注册类的TransFormer调用和retransformClasses函数进行类的重加载。


包括本工具,以及业界比较有名的线上监控工具Arthas、SkyWalking就是这么用的。
字节码生成技术

JavaAgent代理需要修改Class的字节码,对于JavaAgent来讲,任何Class文件在加载时,都要经过premain或agentmain这一代码转换环节。通过一系列的TransFormer转换,Class字节码文件流最终转变为我们期望的代码实现,然后被加载到JVM中。修改Class字节码文件流的动作是在Transformer中进行的。
通常来讲修改字节码可以通过虚拟机的命令(iconst…iadd…)来执行,但这样拼字节码未免太过于麻烦了。
麻烦那就找帮手,找可以修改字节码的工具,此类工具常见的有[ASM](ASM)、JavaAssist、[CGLIB](https://github.com/cglib/cglib)、[ByteBuddy](runtime code generation for the Java virtual machine)等。
说明一下,这里仅仅从好用的角度选择了JavaAssist,关于这几种字节码生成工具的对比以及性能测试,我没有执行过,网上有很多关于这几种字节码生成工具的测试结论,有兴趣的可以搜出来看看或者自己对比一下。
JavaAssist

Javassist(Java 编程助手)使 Java 字节码操作变得简单。它是一个用于在 Java 中编辑字节码的类库;它使 Java 程序能够在运行时定义一个新类,并在 JVM 加载类文件时修改它。与其他类似的字节码编辑器不同,Javassist 提供了两个级别的 API:源代码级别和字节码级别。如果用户使用源代码级 API,他们可以在不知道 Java 字节码规范的情况下编辑类文件。整个 API 仅使用 Java 语言的词汇设计。您甚至可以以源文本的形式指定插入的字节码; Javassist 即时编译它。另一方面,字节码级别的 API 允许用户像其他编辑器一样直接编辑类文件。
上面是[JavaAssist官网](Javassist by jboss-javassist)首页关于JavaAssist的介绍,简单说,Javassist是一个开源的分析、编辑和创建Java字节码的类库。其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成。
Javaassist核心模块

Javaassist中的核心模块是ClassPool、CtClass、CtMethod及CtField这几个类,下面简单介绍这些类的主要作用。

  • ClassPool:一个基于HashMap实现的CtClass对象容器,其中键 是 类 名 称 , 值 是 表 示 该 类 的 CtClass 对 象 。 默 认 的ClassPool使用与底层JVM相同的类路径,因此在某些情况下,可能需要向ClassPool添加类路径或类字节。
  • CtClass:表示一个类,这些CtClass对象可以从ClassPool中获得。
  • CtMethods:表示类中的方法。
  • CtFields:表示类中的字段。
JavaAssit的使用流程




JavaAssit执行流程

实现方案

从上面的介绍来看,技术已经准备好,接下来就是结合我们自己的需求来实现逻辑了。



规范监测工具的实现逻辑


  • 首先,我们会获取应用的PID用于后续将作为监测目标。
接下来,通过Agent实现对信息的挖取(如mysql的表结构、redis的key、日志的打印内容等)。这里挖取到的信息将作为监测项与规则池中的规则进行对比,不符合规则的内容就被记录下来,并且发送到IT运维平台。
最后,将Agent通过VirtualMachine加载到应用进程中。
为了方便功能的划分以及后续的扩展,将Agent处理逻辑分为四块,包含Transformer,Connector,RuleChecker,ResultSender。

  • Transformer,拦截应用对Mysql和Redis的连接,获取连接信息,并通过此方法实现对Class的字节码进行修改,加入监测逻辑处理。
  • Connector,给Transformer调用的,封装对应的redis和mysql等的连接和查询方法。
  • RuleChecker,获取redis和mysql的数据和结构,基于规则池(规范的实例化,可扩展)对数据和结构进行检查并形成检查结果。
  • ResultSender,收集检查结果,并发送到IT运维平台。
Q&A

前面掰掰了这么多,还没有讲到这个工具是如何使用的、以及它的性能如何、它的结果的用途等。简单的,就以几个问答的方式来说明,如果有同学有疑问或者想更进一步了解的,也可以提出新的问题,我会附加在问答的后面。
问:怎么得到待扫描的redis、mysql的具体数据呢?
我们将agent附着在现有的进程中,通过agent的transformer插入到mysql、redis的连接池代码获取到了mysql和redis的连接信息,通过这个链接信息我们可以连接到对应的mysql和redis,从而可以读取redis和mysql的设计数据、使用情况等,以及获取日志打印的内容。
问:它是如何工作的,用户需要做什么?
分为两个阶段,第一个阶段是扫描,第二个阶段是处理。
第一个阶段,这个阶段基本上不需要用户参与,只要有提供服务器IP地址,我们部署agent到指定服务器后会定时对指定目录进行扫描,统一将扫描结果发送至运维平台中(目前没有页面,下一步要开发查询及处理页面)。
第二个阶段,这个阶段主要对扫描结果的处理,这里处理就要看具体的规范如何落实了,暂且搁置,后面对规范落地的措施到位后按照具体要求执行。
问:如果附着在现有应用的进程中,那会不会导致每个应用都有附加的性能压力?
为保证不影响原应用的性能,我们将工具做成了一次性执行的代理程序,意思是执行完一次就会停掉。下次执行的时候会重新启动程序,也不会在忙时执行;所以这个agent所占用的内存和CPU可以忽略。
问:会监测指定IP上的所有进程吗?
目前仅针对java进程进行监测,且默认按照标准部署目录来扫描(如果有特殊路径,需要在启动脚本中特殊指定),目录默认是/data/appsystems/,这个目录下的所有应用进程将被扫描并被监测到。
问:那后续某个进程用的mysql或者redis、logs的内容或者格式在你扫描之后有不规范的现象,是不是就需要重新启动这个应用程序。
原则上是要重新启动,但是不需要用户介入,我们会定时(默认7天)自动启动一次以获取最新的信息。
问:检查的内容是什么,检查的依据是什么?
检查的内容目前就只含有mysql的设计使用规范、reids的bigKey以及日志的打印规范。我们会将各类规范制作成一个规则库,并将规则库接入规则引擎,用于规范的比对与分析。
redis,bigKey的使用,扫描redis的Key;对于大于10M的redis对象被认定为bigKey。另外对于hashset类型的存储,当filed对应的列表大于10w条时,认为是bigKey。
mysql,只要检查一些设计规范及使用规范
logs,对日志打印内容进行解析,并将解析结果与日志打印规范比对与分析。
后续可能根据需要陆续接入业内各项规范,并制作对应的Transfomer加入到此工具中,不断完善与精进。
<hr/>有对软件规范、架构设计、项目分析需求的朋友也可以单独和我聊聊。我是IT屠狗辈,分享各个层面的软件、系统、工程、架构方面的知识。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|开发者网络

GMT+8, 2025-4-9 14:44 , Processed in 0.098054 second(s), 23 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表