【发布时间】:2020-05-26 06:25:29
【问题描述】:
我对 Spring 框架有些陌生。我有一个用 Spring (4.2.1) 编写的 Web 应用程序。我正在尝试使用 Micrometer 库公开指标,并将使用 Prometheus 进行抓取。
应用程序的相关结构是这样的:
- 核心模块 (JAR)
- 网络服务模块(WAR)
我创建了一个 PrometheusService 类,它是 core-module 中定义的一个 bean。在 bean 内部定义的是 PrometheusMeterRegistry 和 Counter:
@Service
public class PrometheusService {
private static PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
private static Counter newAssetCounter = Counter
.builder("new_asset_counter")
.description("count of created assets")
.tags("region", "na")
.register(registry);
public PrometheusService() {
new JvmMemoryMetrics().bindTo(registry);
new DiskSpaceMetrics(new File("/")).bindTo(registry);
new ProcessorMetrics().bindTo(registry);
new UptimeMetrics().bindTo(registry);
}
public static PrometheusMeterRegistry getRegistry() {
return registry;
}
public Counter getNewAssetCounter() {
return this.newAssetCounter;
}
}
我创建了 MetricsResource,它是一个暴露 /metrics 端点的 HttpServlet。当尝试@Autowire PrometheusService bean 时,这里总是为空。快速搜索告诉我 HttpServlet 不是由 Spring 管理的。如果我想@Autowire,我需要添加如下内容:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(webApplicationContext);
现在,我能够@Autowire Servlet 中的 PrometheusService bean。
在 bean 中定义的 Counter 在核心模块中递增。 MetricsResource doGet 方法写入存储在PrometheusMeterRegistry 中的指标。
@WebServlet("/metrics")
public class MetricsResource extends HttpServlet {
private PrometheusService promService; // @Autowired
private PrometheusMeterRegistry registry;
@Override
public void init() throws ServletException {
super.init();
// SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(webApplicationContext);
// promService = (PrometheusService) getServletContext().getAttribute("prometheusService");
// WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
// AutowireCapableBeanFactory ctx = context.getAutowireCapableBeanFactory();
// ctx.autowireBean(this);
}
@Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
resp.setStatus(HttpServletResponse.SC_OK);
resp.setContentType(TextFormat.CONTENT_TYPE_004);
registry = promService.getRegistry();
Writer writer = resp.getWriter();
try {
registry.scrape(writer);
writer.flush();
} finally {
writer.close();
}
}
}
问题在于,Counter 的值在 /metrics 端点处始终为 0。
无论是 @Autowired 还是我手动尝试获取 bean。
这怎么可能?我的 PrometheusService bean 是一个单例。甚至 PrometheusMeterRegistry 和 Counter 都被标记为静态,那么为什么我的 servlet 中会出现不同的对象呢?经过一番搜索,我发现 Spring 将为每个容器创建一个单例 bean。所以我假设这里发生的是两个容器或上下文。一个主应用程序上下文和一个 servlet 上下文。
我尝试过的一些事情:
制作 PrometheusService 实现ApplicationContextAware
使用实现 ApplicationContextAware 并返回 bean 的 ServiceLocator 类
将上下文参数添加到 web.xml
在 app-context.xml
中使用 ServletContextAttributeExporter
使用WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext())
我继续获取对象的新实例。我想要做的就是能够使用 Micrometer 创建和公开自定义指标。我的方法有缺陷吗?如何从我的 HttpServlet 中访问正确的 bean?
Spring dependency injection to other instance
http://senthadev.com/sharing-spring-container-between-modules-in-a-web-application.html
Spring dependency injection to other instance
ApplicationContext and ServletContext
【问题讨论】:
-
我认为,需要在 ServletContextListener 中设置 ApplicationContext。此答案中提供了代码示例:stackoverflow.com/questions/18745770/…
标签: java spring servlets dependency-injection