Soul網關學習Redirect插件原理解析

介紹

Soul 網關在對目標服務進行代理調用的時候,能夠使用 redirect 插件來重定向請求。其中包含兩種場景:一種把 redirectUrl 配置爲第三方URL 地址,直接使用 308 進行轉發跳轉,另外一種是把 redirectUrl 配置以 / 開頭的轉發到網關自身。web

插件配置

  • soul-admin –> 插件管理 –> redirect,設置爲開啓。
  • soul-bootstrap 項目的 pom.xml 文件中添加 redirectmaven 依賴。
  • soul- admin 後臺設置選擇器規則,只有匹配的請求,纔會進行轉發和重定向,請詳細看:選擇器規則

Maven 依賴

soul-bootstrap 工程的 pom.xml 文件中添加插件依賴。spring

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

複製代碼

場景

顧名思義,redirect 插件就是對 uri 的從新轉發和重定向。bootstrap

重定向

  • 咱們在 Rule 配置自定義路徑時,應該爲一個可達的服務路徑。
  • 當匹配到請求後,根據自定義的路徑,Soul 網關會進行 308 服務跳轉。

重定向配置

網關自身接口轉發

  • 當知足匹配規則時,服務內部會使用 DispatcherHandler 內部接口轉發。
  • 要實現網關自身接口轉發,咱們須要在配置路徑使用 / 做爲前綴開始,具體配置以下圖。

自身接口轉發

源碼解析

在解析 redirect 重定向源碼以前,有必要說一些大前提,咱們明白 Soul 網關基於 SpringBoot WebFlux 實現,其中對於 WebFlux 若是默認什麼都不配置,請求會默認執行 DispatcherHandler 處理,這個是響應式 MVC 的處理核心,能夠看一下初始化:markdown

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 相關
  this.handlerMappings = Collections.unmodifiableList(mappings);
  Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
  // handlerAdapter 相關
  this.handlerAdapters = new ArrayList(adapterBeans.values());
  AnnotationAwareOrderComparator.sort(this.handlerAdapters);
  Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerResultHandler.class, true, false);
  // resultHandler 相關
  this.resultHandlers = new ArrayList(beans.values());
  AnnotationAwareOrderComparator.sort(this.resultHandlers);
}

複製代碼

再以後就是咱們熟悉的 MVC 核心處理 DispatcherHandler#handle 方法app

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);
    });
}

複製代碼

搞清楚默認 DispatcherHandler 如何處理,咱們再來講一下 Soul 網關,SoulWebHandler 實現了 WebHandler 接口,再把 BeanName 聲明爲 webHandler 替代了以前 DispatcherHandler 註冊成默認處理 handlermaven

@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);
}

複製代碼

到此爲止咱們明白了,默認請求都經過了 SoulWebHandler#handle 處理,若是咱們須要轉發到網關自身的 MVC 如何作呢?下面經過初始化RedirectPlugin 的時候把 DispatcherHandler 注入,根據具體請求再由 DispatcherHandler 分發,具體核心代碼以下:ide

@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);
  }
  // 處理以 / 開頭自身轉發
  if (redirectHandle.getRedirectURI().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 {
    // 不然就 308 跳轉
    ServerHttpResponse response = exchange.getResponse();
    response.setStatusCode(HttpStatus.PERMANENT_REDIRECT);
    response.getHeaders().add(HttpHeaders.LOCATION, redirectHandle.getRedirectURI());
    return response.setComplete();
  }
}

複製代碼

參考連接:

本文使用 文章同步助手 同步spring-boot

相關文章
相關標籤/搜索