JMX Exporter 配置说明
配置可以参考文档 github.com/prometheus/…
配置结合源码
exporter通过MBeanServer获取MBeanName,查询条件为whitelistObjectNames配置的值,若是没有配置值,则默认查询全部beanName, whitelistObjectNames的配置可以支持正则表达式,若配置的值为精确值,则直接查询Mbean直接返回。
读取whitelistObjectNames配置
if (yamlConfig.containsKey("whitelistObjectNames")) {
List<Object> names = (List<Object>) yamlConfig.get("whitelistObjectNames");
for(Object name : names) {
cfg.whitelistObjectNames.add(new ObjectName((String)name));
}
} else {
//没有配置则加入null进whitelistObjectNames列表中
cfg.whitelistObjectNames.add(null);
}
当我们调用exporter暴露的接口时,会调用JmxScraper的 doScrape()方法
public void doScrape() throws Exception {
MBeanServerConnection beanConn;
JMXConnector jmxc = null;
//若没有配置jmxUrl则本地获取连接
if (jmxUrl.isEmpty()) {
beanConn = ManagementFactory.getPlatformMBeanServer();
} else {
//若配置jmxUrl则远程连接
Map<String, Object> environment = new HashMap<String, Object>();
if (username != null && username.length() != 0 && password != null && password.length() != 0) {
String[] credent = new String[] {username, password};
environment.put(javax.management.remote.JMXConnector.CREDENTIALS, credent);
}
if (ssl) {
environment.put(Context.SECURITY_PROTOCOL, "ssl");
SslRMIClientSocketFactory clientSocketFactory = new SslRMIClientSocketFactory();
environment.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, clientSocketFactory);
environment.put("com.sun.jndi.rmi.factory.socket", clientSocketFactory);
}
jmxc = JMXConnectorFactory.connect(new JMXServiceURL(jmxUrl), environment);
beanConn = jmxc.getMBeanServerConnection();
}
try {
// Query MBean names, see #89 for reasons queryMBeans() is used instead of queryNames()
Set<ObjectName> mBeanNames = new HashSet<ObjectName>();
//若是whitelistObjectNames没有配置,默认配置为[null],beanConn.queryMBeans(null,null)查全部mBeanName,
//配置了whitelistObjectNames则按名称检索
for (ObjectName name : whitelistObjectNames) {
for (ObjectInstance instance : beanConn.queryMBeans(name, null)) {
mBeanNames.add(instance.getObjectName());
}
}
//若是配置了blacklistObjectNames则移除黑名单中的MbeanName,由此可以看出,blacklistObjectNames的配置只是影响接口返回参数,但是检索MbeanName还是正常检索
for (ObjectName name : blacklistObjectNames) {
for (ObjectInstance instance : beanConn.queryMBeans(name, null)) {
mBeanNames.remove(instance.getObjectName());
}
}
// Now that we have *only* the whitelisted mBeans, remove any old ones from the cache:
jmxMBeanPropertyCache.onlyKeepMBeans(mBeanNames);
for (ObjectName objectName : mBeanNames) {
long start = System.nanoTime();
//通过beanName查询对应的指标
scrapeBean(beanConn, objectName);
logger.fine("TIME: " + (System.nanoTime() - start) + " ns for " + objectName.toString());
}
} finally {
if (jmxc != null) {
jmxc.close();
}
}
}
跟踪queryMBeans方法,最后会调Register的query()用方法
public Set<NamedObject> query(ObjectName pattern, QueryExp query) {
final Set<NamedObject> result = new HashSet<NamedObject>();
// The following filter cases are considered:
// null, "", "*:*" : names in all domains
// ":*", ":[key=value],*" : names in defaultDomain
// "domain:*", "domain:[key=value],*" : names in the specified domain
// Surely one of the most frequent cases ... query on the whole world
ObjectName name;
//若是白名单中的beanName为空
if (pattern == null ||
pattern.getCanonicalName().length() == 0 ||
pattern.equals(ObjectName.WILDCARD))
name = ObjectName.WILDCARD;
else name = pattern;
lock.readLock().lock();
try {
// If pattern is not a pattern, retrieve this mbean !
if (!name.isPattern()) {
final NamedObject no = retrieveNamedObject(name);
if (no != null) result.add(no);
return result;
}
// All names in all domains
if (name == ObjectName.WILDCARD) {
for (Map<String,NamedObject> moiTb : domainTb.values()) {
result.addAll(moiTb.values());
}
return result;
}
final String canonical_key_property_list_string =
name.getCanonicalKeyPropertyListString();
final boolean allNames =
(canonical_key_property_list_string.length()==0);
final ObjectNamePattern namePattern =
(allNames?null:new ObjectNamePattern(name));
// All names in default domain
if (name.getDomain().length() == 0) {
final Map<String,NamedObject> moiTb = domainTb.get(domain);
if (allNames)
result.addAll(moiTb.values());
else
addAllMatching(moiTb, result, namePattern);
return result;
}
if (!name.isDomainPattern()) {
final Map<String,NamedObject> moiTb = domainTb.get(name.getDomain());
if (moiTb == null) return Collections.emptySet();
if (allNames)
result.addAll(moiTb.values());
else
addAllMatching(moiTb, result, namePattern);
return result;
}
// Pattern matching in the domain name (*, ?)
final String dom2Match = name.getDomain();
for (String dom : domainTb.keySet()) {
if (Util.wildmatch(dom, dom2Match)) {
final Map<String,NamedObject> moiTb = domainTb.get(dom);
if (allNames)
result.addAll(moiTb.values());
else
addAllMatching(moiTb, result, namePattern);
}
}
return result;
} finally {
lock.readLock().unlock();
}
}
从上面的代码中可以得到结论: 配置 whitelistObjectNames参数可以使查询bean的效率能够提高,避免全部查询。