背景:此前以javaagent的方式集成了prometheus jvm客户端,jmx-exporter,现需要在此基础上加几个自定义参数:
Counter newcounter = Counter.build()
.name(key)
.help(key + "-help")
.register();
运行后,prometheus取不到这个key的数据,深入调查。
jmx-exporter这个工程写了premain方法:


进行了一些Exporter的注册,特别是jvm的一些指标,接下去看下注册的代码:

最终由CollectorRegistery 静态成员变量 defaultRegistry来注册,也就是说一个类加载器所有exporter的注册都统一进行
javaagent由appclassloader加载,其它所有jar包由Saturn Job classloader加载,这个类加载器父加载器为null,则与appclassloader相互之间类是不可见的。
故javaagent和主业务指标不能共用一个httpserver对prometheus提供metric
此前的register代码虽然也注册了,但与jvm参数等所在不同的类加载器,两者注册到了两个注册中心,且这个新的未以http的形式发布给prometheus,用jvm的那个exporter由于两者类加载器隔离自然是跟踪不到这些自定义key的。
解决:
1 模仿jmx-exporter,新开一个httpserver:
new HTTPServer(socket, CollectorRegistry.defaultRegistry, true);

public class PromController {
private static final Logger LOGGER = LoggerFactory.getLogger(PromController.class);
private static final ConcurrentHashMap<String, Counter> mapCounter = new ConcurrentHashMap<>();
//线程池
private static final ExecutorService POOL = new ThreadPoolExecutor(
2, 4, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(20),
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
LOGGER.warn("放弃线程");
}
});
private static HTTPServer server = null;
static {
int cc = 0;
int port = 9892;
while (true) {
if(cc > 10) {
LOGGER.error("放弃监听prom:{}", port);
server = null;
break;
}
InetSocketAddress socket = new InetSocketAddress("0.0.0.0", port);
try {
server = new HTTPServer(socket, CollectorRegistry.defaultRegistry, true);
LOGGER.info("成功监听prom:{}", port);
break;
} catch (IOException e) {
LOGGER.error("{} {}", e.getMessage(), ++cc);
port += 10;
}
}
}
public static void main(String []args) throws InterruptedException {
while (true) {
putPromCounter("xxx");
Thread.sleep(1000);
}
}
public static void putPromCounter(final String key) {
if(server == null)
return;
POOL.submit(new Runnable() {
@Override
public void run() {
// try {
// Class<?> cCollectorRegistry = Thread.currentThread().getContextClassLoader().loadClass("io.prometheus.jmx.shaded.io.prometheus.client.CollectorRegistry");
// Class<?> cCounter = Thread.currentThread().getContextClassLoader().loadClass("io.prometheus.jmx.shaded.io.prometheus.client.Counter");
// Class<?> cCounterBuilder = Thread.currentThread().getContextClassLoader().loadClass("io.prometheus.jmx.shaded.io.prometheus.client.Counter$Builder");
// LOGGER.info("CollectorRegistry: {}", cCollectorRegistry.getProtectionDomain().getCodeSource().getLocation());
// LOGGER.info("Counter: {}", cCounter.getProtectionDomain().getCodeSource().getLocation());
// LOGGER.info("CounterBuilder: {}", cCounterBuilder.getProtectionDomain().getCodeSource().getLocation());
// } catch (ClassNotFoundException e) {
// LOGGER.info("no class ");
// }
Counter counter = mapCounter.get(key);
if(counter == null) {
LOGGER.info("new prom counter {}", key);
Counter newcounter = Counter.build()
.name(key)
.help(key + "-help")
.register();
mapCounter.putIfAbsent(key, newcounter);
counter = mapCounter.get(key);
}
counter.inc();
}
});
}
}
2 prometheus服务端加入对此服务器的拉取
84 - job_name: 'jmx-executor-sym' 85 scrape_interval: 10s 86 static_configs: 87 - targets: ['192.168.57.234:8892'] 88 labels: 89 instance: executor-sym
当然,也可以直接使用prom的client maven:
基于Grafana和Prometheus的监视系统(3):java客户端使用
https://www.jianshu.com/p/60c6d6cb4c49
附属一个jmx-exporter源码分析:https://blog.csdn.net/qqqq0199181/article/details/83792364
