Fix the issue with repeatedly building plugin RouterFunction for CustomEndpoints (#4890)

* fix: OOM occured when using ab to test custom endpoints provided by plugin

* refactor: custom endpoints to rotuer function register for plugin

* refactor: bean post processor register

* Register AggregatedRouterFunction bean instead of adding bean factory post processor

* Remove debug lines

---------

Co-authored-by: John Niang <johnniang@foxmail.com>
pull/4910/head
guqing 2023-11-24 08:31:35 +02:00 committed by GitHub
parent 4ee4a8fd36
commit 57a1f2ec18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 49 additions and 29 deletions

View File

@ -0,0 +1,38 @@
package run.halo.app.plugin;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import run.halo.app.core.extension.endpoint.CustomEndpoint;
import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder;
/**
* Aggregated router function built from all custom endpoints.
*
* @author johnniang
*/
public class AggregatedRouterFunction implements RouterFunction<ServerResponse> {
private final RouterFunction<ServerResponse> aggregated;
public AggregatedRouterFunction(ObjectProvider<CustomEndpoint> customEndpoints) {
var builder = new CustomEndpointsBuilder();
customEndpoints.orderedStream()
.forEach(builder::add);
this.aggregated = builder.build();
}
@Override
public Mono<HandlerFunction<ServerResponse>> route(ServerRequest request) {
return aggregated.route(request);
}
@Override
public void accept(RouterFunctions.Visitor visitor) {
this.aggregated.accept(visitor);
}
}

View File

@ -36,6 +36,7 @@ import run.halo.app.infra.properties.HaloProperties;
*/
@Slf4j
public class PluginApplicationInitializer {
protected final HaloPluginManager haloPluginManager;
private final ExtensionContextRegistry contextRegistry = ExtensionContextRegistry.getInstance();
@ -89,6 +90,8 @@ public class PluginApplicationInitializer {
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
stopWatch.stop();
pluginApplicationContext.registerBean(AggregatedRouterFunction.class);
beanFactory.registerSingleton("pluginContext", createPluginContext(plugin));
// TODO deprecated
beanFactory.registerSingleton("pluginWrapper", haloPluginManager.getPlugin(pluginId));

View File

@ -2,7 +2,7 @@ package run.halo.app.plugin;
import static run.halo.app.plugin.ExtensionContextRegistry.getInstance;
import java.util.ArrayList;
import com.google.common.collect.Iterables;
import java.util.List;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.lang.NonNull;
@ -14,8 +14,6 @@ import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import run.halo.app.core.extension.endpoint.CustomEndpoint;
import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder;
import run.halo.app.plugin.resources.ReverseProxyRouterFunctionRegistry;
/**
@ -48,7 +46,7 @@ public class PluginCompositeRouterFunction implements RouterFunction<ServerRespo
}
@SuppressWarnings("unchecked")
private List<RouterFunction<ServerResponse>> routerFunctions() {
private Iterable<RouterFunction<ServerResponse>> routerFunctions() {
getInstance().acquireReadLock();
try {
List<PluginApplicationContext> contexts = getInstance().getPluginApplicationContexts()
@ -64,18 +62,7 @@ public class PluginCompositeRouterFunction implements RouterFunction<ServerRespo
.toList();
var reverseProxies = reverseProxyRouterFunctionFactory.getRouterFunctions();
var endpointBuilder = new CustomEndpointsBuilder();
contexts.forEach(context -> context.getBeanProvider(CustomEndpoint.class)
.orderedStream()
.forEach(endpointBuilder::add));
var customEndpoint = endpointBuilder.build();
List<RouterFunction<ServerResponse>> routerFunctions =
new ArrayList<>(rawRouterFunctions.size() + reverseProxies.size() + 1);
routerFunctions.addAll(rawRouterFunctions);
routerFunctions.addAll(reverseProxies);
routerFunctions.add(customEndpoint);
return routerFunctions;
return Iterables.concat(rawRouterFunctions, reverseProxies);
} finally {
getInstance().releaseReadLock();
}

View File

@ -1,24 +1,23 @@
package run.halo.app.plugin.event;
import lombok.Getter;
import org.pf4j.PluginWrapper;
import org.springframework.context.ApplicationEvent;
import org.springframework.util.Assert;
/**
* This event will be published to <b>application context</b> once plugin is started.
*
* @author guqing
*/
@Getter
public class HaloPluginStartedEvent extends ApplicationEvent {
private final PluginWrapper plugin;
public HaloPluginStartedEvent(Object source, PluginWrapper plugin) {
super(source);
Assert.notNull(plugin, "Plugin must not be null.");
this.plugin = plugin;
}
public PluginWrapper getPlugin() {
return plugin;
}
}

View File

@ -24,7 +24,6 @@ import org.springframework.web.reactive.function.server.support.RouterFunctionMa
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import run.halo.app.core.extension.endpoint.CustomEndpoint;
import run.halo.app.plugin.resources.ReverseProxyRouterFunctionRegistry;
/**
@ -43,9 +42,6 @@ class PluginCompositeRouterFunctionTest {
@Mock
ObjectProvider<RouterFunction> rawRouterFunctionsProvider;
@Mock
ObjectProvider<CustomEndpoint> customEndpointsProvider;
@InjectMocks
PluginCompositeRouterFunction compositeRouterFunction;
@ -59,11 +55,9 @@ class PluginCompositeRouterFunctionTest {
ExtensionContextRegistry.getInstance().register("fake-plugin", fakeContext);
when(rawRouterFunctionsProvider.orderedStream()).thenReturn(Stream.empty());
when(customEndpointsProvider.orderedStream()).thenReturn(Stream.empty());
when(fakeContext.getBeanProvider(RouterFunction.class))
.thenReturn(rawRouterFunctionsProvider);
when(fakeContext.getBeanProvider(CustomEndpoint.class)).thenReturn(customEndpointsProvider);
compositeRouterFunction =
new PluginCompositeRouterFunction(reverseProxyRouterFunctionRegistry);
@ -93,11 +87,10 @@ class PluginCompositeRouterFunctionTest {
.verify();
verify(rawRouterFunctionsProvider).orderedStream();
verify(customEndpointsProvider).orderedStream();
}
private ServerWebExchange createExchange(String urlTemplate) {
return MockServerWebExchange.from(MockServerHttpRequest.get(urlTemplate));
}
}
}