mirror of https://github.com/halo-dev/halo
perf: add caching for system configuration fetcher to enhance performance (#7100)
#### What type of PR is this? /kind improvement /area core /milestone 2.20.x #### What this PR does / why we need it: 为系统配置获取增加缓存以提高路由和主题模板渲染的速度 #### Special notes for your reviewer: 1. 系统能正确初始化 2. 测试修改系统配置后 http://localhost:8090/actuator/globalinfo 和主题端 `${site}` 是否都是新的 3. 更改了文章路由规则后能正确调整到新的规则 #### Does this PR introduce a user-facing change? ```release-note 为系统配置的获取增加缓存以提高路由和主题模板渲染的速度 ```pull/7094/head^2
parent
7bd9408519
commit
2b4d1ab8d8
|
@ -76,7 +76,7 @@ public class SystemConfigEndpoint implements CustomEndpoint {
|
|||
private Mono<ServerResponse> updateConfigByGroup(ServerRequest request) {
|
||||
final var group = request.pathVariable("group");
|
||||
return request.bodyToMono(ObjectNode.class)
|
||||
.flatMap(objectNode -> configurableEnvironmentFetcher.getConfigMap()
|
||||
.flatMap(objectNode -> configurableEnvironmentFetcher.loadConfigMap()
|
||||
.flatMap(configMap -> {
|
||||
var data = configMap.getData();
|
||||
data.put(group, JsonUtils.objectToJson(objectNode));
|
||||
|
|
|
@ -295,6 +295,6 @@ public class SystemSettingReconciler implements Reconciler<Reconciler.Request> {
|
|||
}
|
||||
|
||||
private Optional<ConfigMap> getConfigMap(String name) {
|
||||
return environmentFetcher.getConfigMapBlocking();
|
||||
return environmentFetcher.loadConfigMapBlocking();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,28 @@
|
|||
package run.halo.app.infra;
|
||||
|
||||
import static run.halo.app.extension.index.query.QueryFactory.equal;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
import com.github.fge.jsonpatch.JsonPatchException;
|
||||
import com.github.fge.jsonpatch.mergepatch.JsonMergePatch;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.extension.ConfigMap;
|
||||
import run.halo.app.extension.ExtensionMatcher;
|
||||
import run.halo.app.extension.ListOptions;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.extension.controller.Controller;
|
||||
import run.halo.app.extension.controller.ControllerBuilder;
|
||||
import run.halo.app.extension.controller.Reconciler;
|
||||
import run.halo.app.infra.utils.JsonParseException;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
|
||||
|
@ -27,9 +36,10 @@ import run.halo.app.infra.utils.JsonUtils;
|
|||
* @since 2.0.0
|
||||
*/
|
||||
@Component
|
||||
public class SystemConfigurableEnvironmentFetcher {
|
||||
public class SystemConfigurableEnvironmentFetcher implements Reconciler<Reconciler.Request> {
|
||||
private final ReactiveExtensionClient extensionClient;
|
||||
private final ConversionService conversionService;
|
||||
private final AtomicReference<ConfigMap> configMapCache = new AtomicReference<>();
|
||||
|
||||
public SystemConfigurableEnvironmentFetcher(ReactiveExtensionClient extensionClient,
|
||||
ConversionService conversionService) {
|
||||
|
@ -71,31 +81,27 @@ public class SystemConfigurableEnvironmentFetcher {
|
|||
.defaultIfEmpty(Map.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets config map.
|
||||
*
|
||||
* @return a new {@link ConfigMap} named <code>system</code> by json merge patch.
|
||||
*/
|
||||
public Mono<ConfigMap> getConfigMap() {
|
||||
Mono<ConfigMap> mapMono =
|
||||
extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG_DEFAULT);
|
||||
if (mapMono == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
return mapMono.flatMap(systemDefault ->
|
||||
extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG)
|
||||
.map(system -> {
|
||||
Map<String, String> defaultData = systemDefault.getData();
|
||||
Map<String, String> data = system.getData();
|
||||
Map<String, String> mergedData = mergeData(defaultData, data);
|
||||
system.setData(mergedData);
|
||||
return system;
|
||||
})
|
||||
.switchIfEmpty(Mono.just(systemDefault)));
|
||||
return Mono.fromSupplier(configMapCache::get)
|
||||
.switchIfEmpty(Mono.defer(this::loadConfigMapInternal));
|
||||
}
|
||||
|
||||
public Optional<ConfigMap> getConfigMapBlocking() {
|
||||
return getConfigMap().blockOptional();
|
||||
/**
|
||||
* Load the system config map from the extension client.
|
||||
*
|
||||
* @return latest configMap from {@link ReactiveExtensionClient} without any cache.
|
||||
*/
|
||||
public Mono<ConfigMap> loadConfigMap() {
|
||||
return loadConfigMapInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the system config map without any cache.
|
||||
*
|
||||
* @return load configMap from {@link ReactiveExtensionClient}
|
||||
*/
|
||||
public Optional<ConfigMap> loadConfigMapBlocking() {
|
||||
return loadConfigMapInternal().blockOptional();
|
||||
}
|
||||
|
||||
private Map<String, String> mergeData(Map<String, String> defaultData,
|
||||
|
@ -132,7 +138,7 @@ public class SystemConfigurableEnvironmentFetcher {
|
|||
return copiedDefault;
|
||||
}
|
||||
|
||||
String mergeRemappingFunction(String dataV, String defaultV) {
|
||||
private String mergeRemappingFunction(String dataV, String defaultV) {
|
||||
JsonNode dataJsonValue = nullSafeToJsonNode(dataV);
|
||||
// original
|
||||
JsonNode defaultJsonValue = nullSafeToJsonNode(defaultV);
|
||||
|
@ -147,8 +153,57 @@ public class SystemConfigurableEnvironmentFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
JsonNode nullSafeToJsonNode(String json) {
|
||||
private JsonNode nullSafeToJsonNode(String json) {
|
||||
return StringUtils.isBlank(json) ? JsonNodeFactory.instance.nullNode()
|
||||
: JsonUtils.jsonToObject(json, JsonNode.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result reconcile(Request request) {
|
||||
loadConfigMapInternal()
|
||||
// should never happen
|
||||
.switchIfEmpty(Mono.error(new IllegalStateException("System configMap not found.")))
|
||||
.doOnNext(configMapCache::set)
|
||||
.block();
|
||||
return Result.doNotRetry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Controller setupWith(ControllerBuilder builder) {
|
||||
ExtensionMatcher matcher = extension -> Objects.equals(extension.getMetadata().getName(),
|
||||
SystemSetting.SYSTEM_CONFIG);
|
||||
return builder
|
||||
.extension(new ConfigMap())
|
||||
.syncAllOnStart(true)
|
||||
.syncAllListOptions(ListOptions.builder()
|
||||
.fieldQuery(equal("metadata.name", SystemSetting.SYSTEM_CONFIG))
|
||||
.build())
|
||||
.onAddMatcher(matcher)
|
||||
.onUpdateMatcher(matcher)
|
||||
.onDeleteMatcher(matcher)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets config map.
|
||||
*
|
||||
* @return a new {@link ConfigMap} named <code>system</code> by json merge patch.
|
||||
*/
|
||||
private Mono<ConfigMap> loadConfigMapInternal() {
|
||||
Mono<ConfigMap> mapMono =
|
||||
extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG_DEFAULT);
|
||||
if (mapMono == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
return mapMono.flatMap(systemDefault ->
|
||||
extensionClient.fetch(ConfigMap.class, SystemSetting.SYSTEM_CONFIG)
|
||||
.map(system -> {
|
||||
Map<String, String> defaultData = systemDefault.getData();
|
||||
Map<String, String> data = system.getData();
|
||||
Map<String, String> mergedData = mergeData(defaultData, data);
|
||||
system.setData(mergedData);
|
||||
return system;
|
||||
})
|
||||
.switchIfEmpty(Mono.just(systemDefault)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ public class SystemSetupEndpoint {
|
|||
)
|
||||
.subscribeOn(Schedulers.boundedElastic());
|
||||
|
||||
var basicConfigMono = Mono.defer(() -> systemConfigFetcher.getConfigMap()
|
||||
var basicConfigMono = Mono.defer(() -> systemConfigFetcher.loadConfigMap()
|
||||
.flatMap(configMap -> {
|
||||
mergeToBasicConfig(body, configMap);
|
||||
return client.update(configMap);
|
||||
|
|
|
@ -18,7 +18,6 @@ import org.mockito.ArgumentCaptor;
|
|||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import run.halo.app.core.reconciler.SystemSettingReconciler;
|
||||
import run.halo.app.extension.ConfigMap;
|
||||
import run.halo.app.extension.ExtensionClient;
|
||||
import run.halo.app.extension.Metadata;
|
||||
|
@ -59,7 +58,7 @@ class SystemSettingReconcilerTest {
|
|||
rules.setArchives("archives-new");
|
||||
return rules;
|
||||
});
|
||||
when(environmentFetcher.getConfigMapBlocking()).thenReturn(Optional.of(configMap));
|
||||
when(environmentFetcher.loadConfigMapBlocking()).thenReturn(Optional.of(configMap));
|
||||
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
|
||||
.thenReturn(Optional.of(configMap));
|
||||
systemSettingReconciler.reconcile(new Reconciler.Request(SystemSetting.SYSTEM_CONFIG));
|
||||
|
@ -83,7 +82,7 @@ class SystemSettingReconcilerTest {
|
|||
rules.setTags("tags-new");
|
||||
return rules;
|
||||
});
|
||||
when(environmentFetcher.getConfigMapBlocking()).thenReturn(Optional.of(configMap));
|
||||
when(environmentFetcher.loadConfigMapBlocking()).thenReturn(Optional.of(configMap));
|
||||
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
|
||||
.thenReturn(Optional.of(configMap));
|
||||
systemSettingReconciler.reconcile(new Reconciler.Request(SystemSetting.SYSTEM_CONFIG));
|
||||
|
@ -104,7 +103,7 @@ class SystemSettingReconcilerTest {
|
|||
rules.setCategories("categories-new");
|
||||
return rules;
|
||||
});
|
||||
when(environmentFetcher.getConfigMapBlocking()).thenReturn(Optional.of(configMap));
|
||||
when(environmentFetcher.loadConfigMapBlocking()).thenReturn(Optional.of(configMap));
|
||||
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
|
||||
.thenReturn(Optional.of(configMap));
|
||||
systemSettingReconciler.reconcile(new Reconciler.Request(SystemSetting.SYSTEM_CONFIG));
|
||||
|
@ -125,7 +124,7 @@ class SystemSettingReconcilerTest {
|
|||
rules.setPost("/post-new/{slug}");
|
||||
return rules;
|
||||
});
|
||||
when(environmentFetcher.getConfigMapBlocking()).thenReturn(Optional.of(configMap));
|
||||
when(environmentFetcher.loadConfigMapBlocking()).thenReturn(Optional.of(configMap));
|
||||
when(client.fetch(eq(ConfigMap.class), eq(SystemSetting.SYSTEM_CONFIG)))
|
||||
.thenReturn(Optional.of(configMap));
|
||||
systemSettingReconciler.reconcile(new Reconciler.Request(SystemSetting.SYSTEM_CONFIG));
|
||||
|
|
|
@ -44,8 +44,8 @@ class SystemConfigurableEnvironmentFetcherTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void getConfigMap() {
|
||||
environmentFetcher.getConfigMap()
|
||||
void loadConfigMap() {
|
||||
environmentFetcher.loadConfigMap()
|
||||
.as(StepVerifier::create)
|
||||
.consumeNextWith(configMap -> {
|
||||
assertThat(configMap.getMetadata().getName())
|
||||
|
|
Loading…
Reference in New Issue