ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Pinpoint Spring 插件实战指南:Spring Beans 方法级追踪与 @Async 异步链路完整配置

Pinpoint Spring 插件实战指南:Spring Beans 方法级追踪与 @Async 异步链路完整配置 Pinpoint Spring 插件实战指南Spring Beans 方法级追踪与 Async 异步链路完整配置【免费下载链接】pinpointAPM, (Application Performance Management) tool for large-scale distributed systems.项目地址: https://gitcode.com/gh_mirrors/pi/pinpoint导读本文档基于 Pinpoint 仓库中 agent-module/plugins/spring/README.md 展开深入讲解 Spring Framework 插件的两大核心能力Spring Beans 方法级调用追踪通过可组合的过滤规则精准选择要埋点的 Bean与Spring Async 异步任务链路追踪在异步线程间传递 Trace 上下文。读者将掌握pinpoint.config中profiler.spring.*全部配置项的语义、过滤规则的逻辑组合OR / AND / OR、component-scan与post-processor两种扫描作用域的选择策略以及自定义 AsyncTaskExecutor / AsyncTask 类的完整方法从而在自己的 Spring 应用中实现高性价比的方法级性能监控。插件概览支持范围与模块结构该插件隶属于 Pinpoint 的 agent-module相关信息摘要如下起始版本Pinpoint 1.5.0支持范围org.springframework/spring-context的版本区间为[5.0, 7.0.max]覆盖 Spring Framework 5/6/7 系列插件坐标pom.xml 中的pinpoint-spring-plugin编译期以provided方式依赖spring-context、spring-web以及javax.servlet-api与jakarta.servlet-api同时兼容 javax 与 jakarta 两代 Servlet 规范功能模块beansSpring Bean 方法级追踪、asyncAsync 异步链路、webSpring Web MVC 请求映射信息补充从 源码目录结构 看插件按beans / async / web三个包组织其中web包下又分为javax与jakarta两套拦截器用于适配不同 Servlet 规范下的 Spring Web MVC。一、Spring Beans 方法级追踪1.1 总开关在pinpoint.config中默认配置文件位于 agent-module/agent/src/main/resources/profiles/release/pinpoint.config约 1057 行起Spring Beans 追踪默认开启# Profile spring-beans profiler.spring.beanstrue该开关在 SpringBeansPlugin.java 中以ENABLE profiler.spring.beans常量读取默认值为true关闭后插件直接跳过字节码增强流程不再注入任何拦截器。⚠️ 注意仅追踪 Bean 的 public 方法。这是插件对方法可见性的明确限制私有方法、包内方法不在追踪范围内。1.2 过滤规则语法filter / value / token 的三层逻辑组合这是本插件最核心、也最容易被误解的配置模型。README 给出的语法为# filters # filter # filter OR filters # filter # value # value AND filter # value # token # token OR token # token # profiler.spring.beans.n.scope [component-scan | post-processor] default is component-scan. # profiler.spring.beans.n.base-packages [package name, ...] # profiler.spring.beans.n.name.pattern [regex pattern, regex:regex pattern, antstyle:antstyle pattern, ...] # profiler.spring.beans.n.class.pattern [regex pattern, regex:regex pattern, antstyle:antstyle pattern, ...] # profiler.spring.beans.n.annotation [annotation name, ...]逐层拆解其语义层级规则含义tokentoken OR token单个配置项内多个值之间是或关系任一个命中即通过valuevalue AND filter同一编号n下多个配置项如base-packages与name.pattern之间是且关系必须全部命中filterfilter OR filters不同编号不同n的多个目标之间是或关系任一目标命中即可也就是说一个完整的过滤判定逻辑为(目标1 的全部条件都满足) OR (目标2 的全部条件都满足) OR ... OR (目标n 的全部条件都满足)该语义在 TargetBeanFilter.java 中有清晰的源码实现外层for循环遍历所有SpringBeansTarget一旦某个 target 的全部过滤维度base-packages、name.pattern、class.pattern、annotation都通过检查condition true即返回true判定为目标 Bean反之continue检查下一个 target。1.3 两种扫描作用域scopeprofiler.spring.beans.n.scope支持两个取值默认值为component-scan取值含义实现机制性能component-scan匹配context:component-scan ... /或ComponentScan扫描出的 Bean拦截org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan快推荐post-processor通过BeanPostProcessor拦截所有 Bean 实例化过程拦截org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory的createBeanInstance与applyBeanPostProcessorsBeforeInstantiationSlow!!!README 原文标注从 SpringBeansPlugin.java 可以看到插件会根据SpringBeansConfig中是否存在对应 scope 的目标决定注入哪个字节码转换器存在COMPONENT_SCAN目标时转换ClassPathBeanDefinitionScanner在doScan(String[])方法上挂载ClassPathDefinitionScannerDoScanInterceptor存在POST_PROCESSOR目标时转换AbstractAutowireCapableBeanFactory在createBeanInstance与applyBeanPostProcessorsBeforeInstantiation两个方法上挂载拦截器。而 SpringBeansTargetScope.java 的解析逻辑表明只有字符串精确等于post-processor忽略大小写才会被识别为POST_PROCESSOR其余任何取值包括空值一律回退为默认的COMPONENT_SCAN。使用建议component-scan覆盖面足够绝大多数现代 Spring 应用都基于注解扫描且性能开销小post-processor可覆盖通过Bean、XMLbean、FactoryBean等方式注册的 Bean但会显著拖慢 Bean 创建路径仅在component-scan覆盖不到的场景如大量 XML 配置、程序化注册才建议使用并务必用base-packages等条件收缩目标范围。1.4 四个过滤维度详解每个目标target可组合以下四个维度①profiler.spring.beans.n.base-packages包前缀profiler.spring.beans.1.base-packagescom.foo, com.bar多个包名用逗号分隔命中规则为类全名的字符串前缀匹配见 TargetBeanFilter.isBasePackage即className.startsWith(basePackage)。例如com.foo会命中com.foo.service.OrderService。②profiler.spring.beans.n.name.patternBean 名模式profiler.spring.beans.1.name.pattern.*Foo, regex:.*Bar, antstyle:*Controller支持三种模式前缀无前缀时按正则表达式处理前缀示例说明无.*Foo直接按正则匹配 Bean 名regex:regex:.*Bar显式声明正则匹配antstyle:antstyle:*ControllerANT 风格路径匹配③profiler.spring.beans.n.class.pattern类名模式profiler.spring.beans.2.class.patternantstyle:com.foo.repository.*Repository, antstyle:com.foo.Service.Main*语法与name.pattern相同但作用于 Bean 的类全名。关于两种 pattern 的编译细节SpringBeansTarget.java 的compilePattern方法揭示了关键差异name.pattern编译时使用的分隔符是/compilePattern(split(namePatternRegex), /)class.pattern编译时使用的分隔符是.compilePattern(split(classPatternRegex), .)带antstyle:前缀的条目使用AntPathMatcher带regex:或无前缀的条目使用RegexPathMatcherPattern.compile。这也解释了 ANT 风格规则的定义# ANT Style pattern rules: # ? - matches on character # * - matches zero or more characters # ** - matches zero or more directories in a path?匹配单个字符*匹配零个或多个字符**匹配路径中的零个或多个目录段对 class.pattern 而言即按.分隔的包层级④profiler.spring.beans.n.annotation类注解profiler.spring.beans.1.annotationorg.springframework.stereotype.Controller,org.springframework.stereotype.Service,org.springframework.stereotype.Repository填写注解的完整类名多个用逗号分隔。命中逻辑见 TargetBeanFilter.isAnnotationTarget不仅匹配 Bean 类上直接声明的注解还通过hasMetaAnnotation/ 注解元数据二次遍历支持元注解meta-annotation匹配——例如Controller、Service、Repository本身都带有元注解Component因此配置org.springframework.stereotype.Component也能命中这三类组件。1.5 完整配置示例README 原文默认值pinpoint.config中默认开启一个component-scan目标按三大常用组件注解筛选profiler.spring.beans.1.scopecomponent-scan profiler.spring.beans.1.base-packages profiler.spring.beans.1.name.pattern profiler.spring.beans.1.class.pattern profiler.spring.beans.1.annotationorg.springframework.stereotype.Controller,org.springframework.stereotype.Service,org.springframework.stereotype.Repository profiler.spring.beans.mark.errorfalseprofiler.spring.beans.mark.error控制是否在 Bean 方法调用出错时标记错误默认false由 SpringBeansConfig.getMarkError 读取并最终传入BeanMethodTransformer用于控制错误标记行为。1.6 多目标组合示例README 原文# 目标 1component-scan 作用域 profiler.spring.beans.1.scopecomponent-scan profiler.spring.beans.1.base-packagescom.foo, com.bar profiler.spring.beans.1.name.pattern.*Foo, regex:.*Bar, antstyle:*Controller profiler.spring.beans.1.class.pattern profiler.spring.beans.1.annotationorg.springframework.stereotype.Controller,org.springframework.stereotype.Service,org.springframework.stereotype.Repository # 目标 2post-processor 作用域用类名 ANT 模式匹配仓储类与主服务类 profiler.spring.beans.2.scopepost-processor profiler.spring.beans.2.base-packagescom.foo profiler.spring.beans.2.name.pattern profiler.spring.beans.2.class.patternantstyle:com.foo.repository.*Repository, antstyle:com.foo.Service.Main* profiler.spring.beans.2.annotation解析上述编号配置的逻辑在 SpringBeansConfig.java 的addTarget中插件通过正则profiler.spring.beans.[0-9].(scope|base-packages|name.pattern|class.pattern|annotation)扫描全部配置项按数字编号归组到同一个SpringBeansTarget再按后缀分派到对应维度。编号不要求连续但同一目标的各配置项必须使用相同编号。无效目标四个维度全部为空会被自动丢弃。1.7 旧版配置的向后兼容SpringBeansConfig构造函数首先调用addBackwardCompatibilityTargetSpringBeansConfig.java支持不带编号后缀的旧式配置键profiler.spring.beans.name.pattern... profiler.spring.beans.class.pattern... profiler.spring.beans.annotation...这三条旧式配置分别对应内部编号-1、-2、-3的目标与新版编号配置合并后统一过滤。如果你的配置文件还在使用这种写法依然可以正常工作但建议迁移到编号式配置以利用 scope / base-packages 等更精细的能力。1.8 源码级实现原理小结结合 TargetBeanFilter 与拦截器实现可以归纳完整追踪流程扫描捕获component-scan作用域通过ClassPathDefinitionScannerDoScanInterceptor在 Spring 扫描doScan时捕获候选 Bean 定义post-processor作用域通过CreateBeanInstanceInterceptor/PostProcessorInterceptor在 Bean 实例化前后捕获目标判定TargetBeanFilter.isTarget(scope, beanName, beanDefinition/clazz)按scope 匹配 → 各维度 AND 校验 → 目标间 OR 汇总判定是否为目标 Bean字节码转换命中目标后BeanMethodTransformer对 Bean 类的方法进行转换注入BeanMethodInterceptor实现方法级调用链追踪去重缓存TargetBeanFilter内部维护一个Cache已转换过的类名会被缓存addTransformed避免同一类被重复增强同时保证线程安全下的转换一致性。对应的单元测试位于 agent-module/plugins/spring/src/test/java/com/navercorp/pinpoint/plugin/spring/beans包括SpringBeansConfigTest配置解析、SpringBeansTargetTest/SpringBeansTargetScopeTest目标与作用域、TargetBeanFilterTest过滤逻辑、BeanMethodInterceptorTest方法拦截可作为理解插件行为的参考样例。二、Spring Async 异步链路追踪2.1 总开关profiler.spring.async.enabletrue该开关由 SpringAsyncConfig.java 读取默认值为true。关闭后SpringAsyncPlugin直接退出不再做任何增强SpringAsyncPlugin.java。2.2 默认覆盖的 AsyncTaskExecutor 类profiler.spring.async.executor.class.names用于追加自定义的 AsyncTaskExecutor 实现逗号分隔的完整类名不支持通配符。以下为默认内置列表README 与 SpringAsyncConfig.java 的DEFAULT_ASYNC_TASK_EXECUTOR一致共 8 个org.springframework.scheduling.concurrent.ConcurrentTaskExecutor org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler org.springframework.core.task.SimpleAsyncTaskExecutor org.springframework.scheduling.quartz.SimpleThreadPoolTaskExecutor org.springframework.core.task.support.TaskExecutorAdapter org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor org.springframework.jca.work.WorkManagerTaskExecutor org.springframework.scheduling.commonj.WorkManagerTaskExecutor覆盖了TaskExecutor接口的常见实现线程池封装ThreadPoolTaskExecutor、ConcurrentTaskExecutor、调度器ThreadPoolTaskScheduler、SimpleThreadPoolTaskExecutor、适配器TaskExecutorAdapter、JCA / CommonJ 企业集成WorkManagerTaskExecutor两种等。2.3 默认覆盖的 AsyncTask 类profiler.spring.async.task.class.names用于追加自定义的异步任务类逗号分隔不支持通配符。默认内置列表SpringAsyncConfig.javaorg.springframework.aop.interceptor.AsyncExecutionInterceptor$1 org.springframework.aop.interceptor.AsyncExecutionInterceptor$$Lambda$ # 适用于 Spring Framework 5.0 及以上其中$1是AsyncExecutionInterceptor内部的任务实现类$$Lambda$是 JDK 8 Lambda 形态的任务类for spring framework 5.0 or later。README 还给出一个扩展示例追踪SimpleBufferingAsyncClientHttpRequest.executeAsync()时需追加其内部任务类# ex. add org.springframework.http.client.SimpleBufferingAsyncClientHttpRequest$1 and # org.springframework.http.client.SimpleBufferingAsyncClientHttpRequest$$Lambda$ # to trace SimpleBufferingAsyncClientHttpRequest.executeAsync() profiler.spring.async.task.class.namesorg.springframework.http.client.SimpleBufferingAsyncClientHttpRequest$1,org.springframework.http.client.SimpleBufferingAsyncClientHttpRequest$$Lambda$该示例说明凡是你希望纳入异步链路追踪的任务类通常是$1/$$Lambda$形式的内部类都需要通过此配置显式追加。2.4 异步链路的底层拦截原理从 SpringAsyncPlugin.java 可以看出 Async 追踪的三段式拦截任务执行器入口对每个 AsyncTaskExecutor 类的submit(Callable)、submitListenable(Callable)方法挂载AsyncTaskExecutorSubmitInterceptor作用域ASYNC_TASK_EXECUTOR_SCOPE对 Spring 7 还额外处理submitCompletable(Callable)源码注释// spring 7印证了插件对 Spring 7 的支持AOP 提交点拦截org.springframework.aop.interceptor.AsyncExecutionAspectSupport#doSubmit(Callable, AsyncTaskExecutor, Class)挂载AsyncExecutionAspectSupportInterceptor在Async方法真正提交到执行器时建立异步上下文任务执行点对每个 AsyncTask 类添加AsyncContextAccessor字段并在其call()方法上挂载TaskCallInterceptor使任务在线程池中真正执行时恢复并延续调用链。由此实现提交线程 → 执行线程的异步上下文传递让Async方法中产生的 Span 与调用方处于同一条分布式调用链上。2.5 组合配置示例profiler.spring.async.enabletrue # 追加自定义 AsyncTaskExecutor默认 8 个类之外 profiler.spring.async.executor.class.namescom.example.async.CustomTaskExecutor # 追加自定义 AsyncTask 类 profiler.spring.async.task.class.namesorg.springframework.http.client.SimpleBufferingAsyncClientHttpRequest$1,org.springframework.http.client.SimpleBufferingAsyncClientHttpRequest$$Lambda$三、验证与测试参考单元测试agent-module/plugins/spring/src/test 覆盖了配置解析、目标过滤与拦截器行为集成测试IT配置仓库在 agent-module/plugins-it/spring-it 中提供了 spring-5 / spring-6 两套集成测试工程。其测试配置文件 pinpoint-spring-bean-test.config 展示了post-processor作用域的真实用法多个目标组合name.patternma.*, outer、class.pattern.*Morae、annotationorg.springframework.stereotype.Component并配有pinpoint-disabled-plugin-test.config用于验证关闭插件的行为差异默认配置落点完整注释版配置可查阅 agent-module/agent/src/main/resources/profiles/release/pinpoint.configprofiler.spring.*段约 1057–1130 行。四、实战要点与注意事项优先使用component-scan作用域现代注解驱动的 Spring 应用推荐用默认的component-scanpost-processor虽然能覆盖 XML / 程序化注册的 Bean但 README 明确标注其性能为Slow!!!务必配合base-packages等条件收缩范围理解三层逻辑关系同一编号内的多个维度是AND需全部满足不同编号之间是OR任一满足即可单个维度内的多个值也是ORpattern 前缀语法name.pattern/class.pattern默认按正则编译可用regex:显式声明用antstyle:启用 ANT 风格匹配class.pattern 的**按.分隔的包层级展开annotation 支持元注解配置Component即可命中Controller/Service/Repository等带Component元注解的类仅追踪 public 方法插件只对 Bean 的 public 方法做方法级增强Async 扩展遵循完整类名 不支持通配符追加自定义执行器/任务类时需给出全限定类名且要同时关注$1与$$Lambda$两种任务类形态配置文件位置以上所有profiler.spring.*配置都写入 agent 的pinpoint.configrelease 与 local profile 均有对应默认段落修改后需重启被监控应用生效。【免费下载链接】pinpointAPM, (Application Performance Management) tool for large-scale distributed systems.项目地址: https://gitcode.com/gh_mirrors/pi/pinpoint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表