experimental optimization to avoid calling potentially slow jmx endpoints too often

This commit is contained in:
Martin Lippert
2018-08-20 17:11:28 +02:00
parent 02f17d2b0a
commit ffcee42a0e

View File

@@ -14,7 +14,6 @@ import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.PlatformManagedObject;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
@@ -23,6 +22,7 @@ import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.TimeUnit;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServerConnection;
@@ -46,6 +46,8 @@ import org.springframework.ide.vscode.commons.util.FunctionWithException;
import org.springframework.ide.vscode.commons.util.MemoizingDisposableSupplier;
import org.springframework.ide.vscode.commons.util.StringUtil;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -64,6 +66,10 @@ public abstract class AbstractSpringBootApp implements SpringBootApp {
private LiveBeansModel cachedBeansModel;
private String cachedBeansModelMD5;
private Cache<String, LiveBeansModel> beansModelCache = CacheBuilder.newBuilder()
.expireAfterWrite(3, TimeUnit.SECONDS)
.build();
// NOTE: Gson-based serialisation replaces the old Jackson ObjectMapper. Not sure if this makes a difference in the long run, but to retain the same output that Jackson Object Mapper
// was generating during serialisation, some configuration in Gson is required, as the default behaviour of Gson is different than Object Mapper.
// Namely: Object Mapper does not escape Html, whereas Gson does by default (for example
@@ -205,29 +211,33 @@ public abstract class AbstractSpringBootApp implements SpringBootApp {
@Override
public LiveBeansModel getBeans() {
try {
String domain = getDomainForActuator();
Object json = getBeansFromActuator(domain);
return beansModelCache.get("liveBeans", () -> {
String domain = getDomainForActuator();
Object json = getBeansFromActuator(domain);
if (json != null) {
String md5 = DigestUtils.md5Hex(json.toString());
if (json != null) {
String md5 = DigestUtils.md5Hex(json.toString());
synchronized(this) {
if (cachedBeansModel == null || !md5.equals(cachedBeansModelMD5)) {
cachedBeansModel = LiveBeansModel.parse(gson.toJson(json));
cachedBeansModelMD5 = md5;
logger.debug("Got {} beans for {}", cachedBeansModel.getBeanNames().size(), this);
} else {
logger.debug("Got {} beans for {} - from cache", cachedBeansModel.getBeanNames().size(), this);
synchronized(AbstractSpringBootApp.this) {
if (cachedBeansModel == null || !md5.equals(cachedBeansModelMD5)) {
cachedBeansModel = LiveBeansModel.parse(gson.toJson(json));
cachedBeansModelMD5 = md5;
logger.debug("Got {} beans for {}", cachedBeansModel.getBeanNames().size(), this);
} else {
logger.debug("Got {} beans for {} - from cache", cachedBeansModel.getBeanNames().size(), this);
}
}
}
return cachedBeansModel;
}
return cachedBeansModel;
}
else {
throw new Exception("not getting any beans from app");
}
});
} catch (Exception e) {
logger.error("Error parsing beans", e);
return LiveBeansModel.builder().build();
}
return LiveBeansModel.builder().build();
}
private Object getBeansFromActuator(String domain) throws Exception {