introduce

The Soul Gateway uses the Redirect plug-in to redirect requests when making proxy calls to target services. There are two scenarios: One is to configure redirectUrl as a third-party URL address and directly use 308 for forwarding; the other is to forward the redirectUrl configuration starting with/to the gateway itself.

The plug-in configuration

  • insoul-admin– > Plug-in Management – >redirect, set it to enable.
  • insoul-bootstrapThe projectpom.xmlAdd to fileredirectmavenRely on.
  • insoul- adminSet the selector rule in the background. Only matching requests will be forwarded and redirected.Selector rule.

Maven rely on

Add the plug-in dependencies to the pom.xml file of the soul-Bootstrap project.

<dependency>
  <groupId>org.dromara</groupId>
  <artifactId>soul-spring-boot-starter-plugin-redirect</artifactId>
  <version>${last.version}</version>
</dependency>

Copy the code

scenario

As the name implies, the Redirect plug-in is simply a redirection and redirection of URIs.

redirect

  • We are inRuleWhen you configure a custom path, it should be an accessible service path.
  • When the request is matched, based on the custom path,Soul gatewayWill be carried out in308Service jump.

The interface of the gateway forwards the packet

  • If the matching rule is met, the service is used internallyDispatcherHandlerInternal interface forwarding.
  • To implement interface forwarding of the gateway itself, we need to use it in the configuration path/Start as a prefix, as shown in the following figure.

The source code parsing

Soul Gateway is based on SpringBoot WebFlux implementation. If WebFlux is not configured at all by default, it is not configured at all. The request is handled by DispatcherHandler by default. This is the core of reactive MVC processing. We can look at initialization:

protected void initStrategies(ApplicationContext context) { Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); ArrayList<HandlerMapping> mappings = new ArrayList(mappingBeans.values()); AnnotationAwareOrderComparator.sort(mappings); / / handlerMapping related this. HandlerMappings = Collections. UnmodifiableList (the mappings); Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false); HandlerAdapters = new ArrayList(adapterBeans.values()); // AdapterBeans.adapters = new ArrayList(adapterBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerAdapters); Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false); // resultHandler correlation this.resulthAndlers = new ArrayList(beans.values()); // resultHandler correlation this.resulthAndlers = new ArrayList(beans.values()); AnnotationAwareOrderComparator.sort(this.resultHandlers); }Copy the code

Then there’s the familiar MVC core that handles the DispatcherHandler#handle method

public Mono<Void> handle(ServerWebExchange exchange) {
    return this.handlerMappings == null ? this.createNotFoundError() : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {
        return mapping.getHandler(exchange);
    }).next().switchIfEmpty(this.createNotFoundError()).flatMap((handler) -> {
        return this.invokeHandler(exchange, handler);
    }).flatMap((result) -> {
        return this.handleResult(exchange, result);
    });
}

Copy the code

Soul Gateway, SoulWebHandler implements the WebHandler interface, BeanName is declared webHandler instead of registering DispatcherHandler as the default handler.

@Bean("webHandler")
public SoulWebHandler soulWebHandler(final ObjectProvider<List<SoulPlugin>> plugins) {
  List<SoulPlugin> pluginList = plugins.getIfAvailable(Collections::emptyList);
  List<SoulPlugin> soulPlugins = pluginList.stream()
    .sorted(Comparator.comparingInt(SoulPlugin::getOrder)).collect(Collectors.toList());
  soulPlugins.forEach(soulPlugin -> log.info("load plugin:[{}] [{}]", soulPlugin.named(), soulPlugin.getClass().getName()));
  return new SoulWebHandler(soulPlugins);
}

Copy the code

So far we know that the default request is handled by SoulWebHandler#handle. What if we need to forward it to the gateway’s own MVC? RedirectPlugin is initialized with DispatcherHandler and then distributed by DispatcherHandler according to the specific request. The specific core code is as follows:

@Override protected Mono<Void> doExecute(final ServerWebExchange exchange, final SoulPluginChain chain, final SelectorData selector, final RuleData rule) { final String handle = rule.getHandle(); final RedirectHandle redirectHandle = GsonUtils.getInstance().fromJson(handle, RedirectHandle.class); if (Objects.isNull(redirectHandle) || StringUtils.isBlank(redirectHandle.getRedirectURI())) { log.error("uri redirect rule can not configuration: {}", handle); return chain.execute(exchange); } / / processing begin with/itself forward the if (redirectHandle. GetRedirectURI (). The startsWith (ROOT_PATH_PREFIX)) {ServerHttpRequest request = exchange.getRequest().mutate() .uri(Objects.requireNonNull(UriUtils.createUri(redirectHandle.getRedirectURI()))).build(); ServerWebExchange mutated = exchange.mutate().request(request).build(); return dispatcherHandler.handle(mutated); } else {// if not, return to ServerHttpResponse response = exchange.getresponse (); response.setStatusCode(HttpStatus.PERMANENT_REDIRECT); response.getHeaders().add(HttpHeaders.LOCATION, redirectHandle.getRedirectURI()); return response.setComplete(); }}Copy the code

Reference links:

  • Design and working principle analysis of Spring WebFlux
  • How Spring WebFlux works

This article uses the article synchronization assistant to synchronize