使用 metrics-servlet 的 InstrumentedFilter 为 Web 应用注入请求级指标监控
可观测性后端【免费下载链接】metrics:chart_with_upwards_trend: Capturing JVM- and application-level metrics. So you know whats going on.项目地址https://gitcode.com/gh_mirrors/met/metrics点击查看免费下载本文以 Dropwizard Metrics 项目中的metrics-servlet模块为核心讲解如何通过一个 Servlet 过滤器InstrumentedFilter为 Web 应用自动采集 HTTP 请求的响应状态码速率、活跃请求数与请求耗时分布。读完本文你将掌握该过滤器的web.xml配置方法、name-prefix多映射命名技巧、MetricRegistry的注入方式并能基于源码理解每个指标背后的实现原理。模块定位一个过滤器解决三类核心监控需求metrics-servlet是当前仓库中的一个独立 Maven 模块其定位在 metrics-servlet/pom.xml 的描述中被明确为 An instrumented filter for servlet environments面向 Servlet 环境的插桩过滤器。它并不要求你改造业务代码而是以标准javax.servlet.Filter的形式挂载到应用的过滤器链上对进出应用的每个 HTTP 请求自动采集三类数据状态码速率Meter对返回的响应状态码分别计数并计算速率例如 200成功、404未找到、500服务端错误等活跃请求数Counter统计当前正在处理中的请求数量请求耗时Timer统计每个请求的耗时分布如均值、中位数、P95、P99 等。模块的实现由三个主类构成均位于 metrics-servlet/src/main/java/com/codahale/metrics/servlet 目录下类名职责InstrumentedFilter过滤器对外入口预置一组默认状态码映射指定默认registry属性名AbstractInstrumentedFilter过滤器核心逻辑负责指标创建、请求计时、状态码上报与异步请求处理InstrumentedFilterContextListenerServletContextListener实现负责把应用的MetricRegistry注入 Servlet 上下文依赖关系上该模块只依赖metrics-core提供Meter、Counter、Timer、MetricRegistry等核心类型并以provided作用域使用javax.servlet-api4.0.1意味着 Servlet API 由你的应用容器如 Tomcat、Jetty提供模块本身打包为 OSGi bundle。快速上手在 web.xml 中启用过滤器默认情况下过滤器会使用com.codahale.metrics.servlet.InstrumentedFilter作为所有指标的基础名称base name。在应用的web.xml中添加如下配置即可启用filter filter-nameinstrumentedFilter/filter-name filter-classcom.codahale.metrics.servlet.InstrumentedFilter/filter-class /filter filter-mapping filter-nameinstrumentedFilter/filter-name url-pattern/*/url-pattern /filter-mapping这里用url-pattern/*/url-pattern将过滤器映射到应用的全部请求。结合源码看init阶段AbstractInstrumentedFilter.java会完成以下指标的创建预置状态码对应的Meter兜底用的otherMetertimeoutsMeter与errorsMeteractiveRequestsCounterrequestsTimer。其中基础名称metricName的解析逻辑为先读取name-prefix初始化参数若未配置或为空字符串则回退到过滤器类名getClass().getName()即com.codahale.metrics.servlet.InstrumentedFilter。多 URL 模式差异化命名name-prefix 参数当应用需要按 URL 模式区分监控指标时可通过过滤器可选的init-param——name-prefix覆盖指标的基础名称。例如单独为/auth/*的认证接口建立一套独立指标filter filter-nameinstrumentedFilter/filter-name filter-classcom.codahale.metrics.servlet.InstrumentedFilter/filter-class init-param param-namename-prefix/param-name param-valueauthentication/param-value /init-param /filter filter-mapping filter-nameinstrumentedFilter/filter-name url-pattern/auth/*/url-pattern /filter-mapping通过为不同url-pattern声明多个同名过滤器、各自指定不同的name-prefix可以让每个 URL 模式拥有独一无二的指标命名空间。例如上述配置下认证接口的指标名将以authentication开头如authentication.requests、authentication.activeRequests、authentication.responseCodes.*与根路径映射的com.codahale.metrics.servlet.InstrumentedFilter.*系列指标互不冲突。从源码实现看name-prefix的读取位于 AbstractInstrumentedFilter.java常量METRIC_PREFIX name-prefix定义了该参数的名称参数值为空时按未配置处理自动回退到类名。注入 MetricRegistryServletContext 属性 上下文监听器过滤器的指标需要写入应用自身的MetricRegistry否则无法与你其余指标汇总统一输出如写入 Graphite、JMX。metrics-servlet约定将MetricRegistry作为 Servlet 上下文ServletContext的一个属性存放属性名为com.codahale.metrics.servlet.InstrumentedFilter.registry。这一点在InstrumentedFilter中通过常量固化InstrumentedFilter.javapublic static final String REGISTRY_ATTRIBUTE InstrumentedFilter.class.getName() .registry;推荐的注入方式是扩展InstrumentedFilterContextListener在容器启动时由监听器把 registry 放入上下文public class MyInstrumentedFilterContextListener extends InstrumentedFilterContextListener { public static final MetricRegistry REGISTRY new MetricRegistry(); Override protected MetricRegistry getMetricRegistry() { return REGISTRY; } }然后在web.xml中注册该监听器listener listener-classcom.example.MyInstrumentedFilterContextListener/listener-class /listener其底层机制在 InstrumentedFilterContextListener.java 中非常直观contextInitialized回调时调用sce.getServletContext().setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE, getMetricRegistry())把抽象方法getMetricRegistry()返回的实例挂到上下文。对应的单元测试 InstrumentedFilterContextListenerTest.java 使用 Mockito 验证了setAttribute(com.codahale.metrics.servlet.InstrumentedFilter.registry, registry)这一行为可作为阅读源码时行为确认的参照。需要留意的是过滤器读取 registry 时的回退行为在getMetricsFactory方法AbstractInstrumentedFilter.java中若上下文属性不存在或不是MetricRegistry实例过滤器会静默地 new 一个全新的MetricRegistry。这意味着漏配监听器时应用不会报错但指标将进入一个独立、无法被统一输出消费的 registry因此务必按上述步骤完成注入。指标全景过滤器会创建哪些指标综合 InstrumentedFilter.java 与 AbstractInstrumentedFilter.java在默认基础名称com.codahale.metrics.servlet.InstrumentedFilter下每个映射会创建如下指标按name(metricName, 子名)拼接即基础名.子名指标名子名部分类型含义requestsTimer所有同步或异步请求的耗时分布activeRequestsCounter当前正在处理中的请求数请求进入doFilter时 1结束时 -1responseCodes.okMeter返回 200 的响应速率responseCodes.createdMeter返回 201 的响应速率responseCodes.noContentMeter返回 204 的响应速率responseCodes.badRequestMeter返回 400 的响应速率responseCodes.notFoundMeter返回 404 的响应速率responseCodes.serverErrorMeter返回 500 的响应速率responseCodes.otherMeter兜底速率统计所有未在上表列出的状态码catch-alltimeoutsMeter异步请求超时次数errorsMeter请求抛出异常IOException、RuntimeException、ServletException的次数其中默认状态码映射由createMeterNamesByStatusCode方法InstrumentedFilter.java定义200→ok、201→created、204→noContent、400→badRequest、404→notFound、500→serverError其余状态码统一计入responseCodes.other。请求处理流程与状态码上报原理doFilter方法AbstractInstrumentedFilter.java刻画了每个请求的完整生命周期将原始HttpServletResponse包装为StatusExposingServletResponse用于捕获最终状态码——由于 Servlet 规范规定未显式设置状态时默认为 200该包装类初始状态为 200并覆写setStatus、sendError等方法记录每次设置activeRequests.inc()递增活跃请求计数requestTimer.time()启动计时调用chain.doFilter(request, wrappedResponse)继续后续过滤器与目标 Servlet若调用链抛出IOException、RuntimeException或ServletException标记error true后重新抛出由容器继续处理也因此errorsMeter统计的是进入过滤器链的请求本身抛出的异常而非 HTTP 500finally块中按请求是否为异步请求分流处理见下节。状态码上报的核心在markMeterForStatusCode方法AbstractInstrumentedFilter.java先查预置的状态码映射表命中则标记对应Meter未命中则标记otherMeter。异步请求Async的正确计时处理由于 Servlet 3.0 异步请求在doFilter返回后可能仍未完成处理AbstractInstrumentedFilter对此做了专门处理。当请求调用了request.startAsync()且未发生错误时过滤器不会立刻结束计时而是注册AsyncResultListenerAbstractInstrumentedFilter.java按异步生命周期事件结算指标onComplete异步处理完成——停止计时、递减activeRequests、按最终响应状态码标记对应MeteronTimeout异步处理超时——停止计时、递减activeRequests、标记timeoutsonError异步出错——停止计时、递减activeRequests、标记errorsonStartAsync空实现用于接收异步启动事件。若容器在onTimeout/onError之后再触发onCompletedone标志位可防止重复结算。这保证了基于异步 Servlet 的长连接、SSE、WebSocket 握手等场景下的耗时数据依然真实可信。依赖配置与自定义扩展方向在 Maven 项目中引入该模块时只需声明对metrics-servlet的依赖Servlet API 由容器提供dependency groupIdio.dropwizard.metrics/groupId artifactIdmetrics-servlet/artifactId /dependency版本可通过 metrics-bom 统一管理模块打包为 OSGi bundle模块名com.codahale.metrics.servletJava 9 模块名见 metrics-servlet/pom.xml 中的javaModuleName属性。如需自定义状态码分类例如为 401、429 单独建 Meter可以从源码结构推断出扩展路径AbstractInstrumentedFilter的受保护构造函数接收registryAttribute、meterNamesByStatusCode映射与otherMetricName三个参数子类传入自定义映射即可复刻InstrumentedFilter的完整能力——这与InstrumentedFilter本身预置默认映射的设计思路一致。最后建议将InstrumentedFilter与仓库中的 metrics-servlets 模块提供指标 HTTP 输出端点或metrics-graphite、metrics-jmx等报告器配合使用即可把上述requests、activeRequests、responseCodes.*指标接入你的监控大盘形成从采集到展示的完整链路。赞分享可观测性后端【免费下载链接】metrics:chart_with_upwards_trend: Capturing JVM- and application-level metrics. So you know whats going on.项目地址https://gitcode.com/gh_mirrors/met/metrics点击查看免费下载相关推荐Sentinel Web Servlet Filter 接入指南为 Java Web 请求注入流控防护Sentinel Web Servlet Filter 接入指南为 Java Web 请求注入流控防护 导读 本文围绕 Sentinel 的 sentinel后端微服务Kingfisher预提交钩子在代码提交前自动检测密钥的终极指南Kingfisher预提交钩子在代码提交前自动检测密钥的终极指南 在软件开发中意外提交敏感信息是常见的安全风险。Kingfisher预提交钩子为您提供了一种终极指南使用Prometheus Operator监控Web服务器请求与错误指标终极指南使用Prometheus Operator监控Web服务器请求与错误指标 Prometheus Operator是一个针对Kubernetes的运营商云原生可观测性上一篇Midday校准限制配置最大调整与最小样本要求的最佳值下一篇Preact Query 的 QueryClientProviderProps 类型详解从 Props 到上下文注入的完整原理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考