spring cloud gateway 啟動(dòng)流程及原理分析

spring cloud gateway 作為新一代的微服務(wù)網(wǎng)關(guān)已經(jīng)發(fā)布了一段時(shí)間,我從7月份開(kāi)始使用到現(xiàn)在已經(jīng)4個(gè)月了。但是我一直處于一種只會(huì)使用,原理一知半解的水平。我們小組作為公司微服務(wù)產(chǎn)品的實(shí)踐者,我自己作為組中一員完成了spring cloud gateway的開(kāi)發(fā),也解決了很多棘手的問(wèn)題,卻對(duì)它的原理和啟動(dòng)流程一知半解,好幾次就是因?yàn)椴涣私馑膯?dòng)流程,導(dǎo)致開(kāi)發(fā)受挫,進(jìn)度緩慢?,F(xiàn)在正值閑時(shí),正好看一下相關(guān)的源碼,理解他的啟動(dòng)流程。本文基于spring cloud Finchley.RELEASE版本,最新的SR2版本一些內(nèi)容有改變,但總體改變不大。

首先是網(wǎng)關(guān)的包結(jié)構(gòu)


其中actuate中定義了一個(gè)叫GatewayControllerEndpoint的類(lèi),這個(gè)類(lèi)提供一些對(duì)外的接口,可以獲取網(wǎng)關(guān)的一些信息,比如路由的信息,改變路由地址等等。

config中定義了一些啟動(dòng)時(shí)去加載的類(lèi),配置路由信息和讀取你的配置文件就在這里完成。

discovery中定義了注冊(cè)中心相關(guān)的內(nèi)容,包括注冊(cè)中心的路由等。

event定義了一些事件他們都繼承自ApplicationEvent,對(duì)事件發(fā)布不了解的可以去看看spring的代碼。

filter中定義了spring cloud gateway實(shí)現(xiàn)的一些過(guò)濾器。

handler中定義了很多Predicate相關(guān)的Factory

route就是我們路由的相關(guān)

support是工具包等。

下面就是gateway啟動(dòng)流程的分析了

config包

網(wǎng)關(guān)啟動(dòng)第一步加載的就是去加載config包下的幾個(gè)類(lèi)。

這幾個(gè)類(lèi)就定義了網(wǎng)關(guān)需要加載的配置項(xiàng)。

在我第一次做網(wǎng)關(guān)開(kāi)發(fā)的時(shí)候我引入了spring-boot-starter-web的依賴(lài),這樣是會(huì)報(bào)錯(cuò)的,因?yàn)間ateway是基于spring-webflux開(kāi)發(fā)的,他依賴(lài)的DispatcherHandler就和我們web里的DispatcherServlet一樣的功能。

這里的GatewayClassPathWarningAutoConfiguration這個(gè)類(lèi),就指明了我們需要什么不需要什么,他加載于GatewayAutoConfiguration之前,如果DispatcherServlet存在,就會(huì)給與警告,同樣的DispatcherHandler不存在也會(huì)警告。


@Configuration

@AutoConfigureBefore(GatewayAutoConfiguration.class)

public class GatewayClassPathWarningAutoConfiguration {

 private static final Log log = LogFactory.getLog(GatewayClassPathWarningAutoConfiguration.class);

 private static final String BORDER = "\n\n**********************************************************\n\n";

 @Configuration

 @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")

 protected static class SpringMvcFoundOnClasspathConfiguration {

  public SpringMvcFoundOnClasspathConfiguration() {

   log.warn(BORDER+"Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. "+

     "Please remove spring-boot-starter-web dependency."+BORDER);

  }

 }

 @Configuration

 @ConditionalOnMissingClass("org.springframework.web.reactive.DispatcherHandler")

 protected static class WebfluxMissingFromClasspathConfiguration {

  public WebfluxMissingFromClasspathConfiguration() {

   log.warn(BORDER+"Spring Webflux is missing from the classpath, which is required for Spring Cloud Gateway at this time. "+

     "Please add spring-boot-starter-webflux dependency."+BORDER);

  }

 }

}

它加載完成之后加載的是GatewayLoadBalancerClientAutoConfiguration這個(gè)是gateway負(fù)載均衡的過(guò)濾器實(shí)現(xiàn)的加載,他將LoadBalancerClientFilter 注入到了容器中,這個(gè)過(guò)濾器后面再說(shuō)。


@Configuration

@ConditionalOnClass({LoadBalancerClient.class, RibbonAutoConfiguration.class, DispatcherHandler.class})

@AutoConfigureAfter(RibbonAutoConfiguration.class)

public class GatewayLoadBalancerClientAutoConfiguration {

 // GlobalFilter beans

 @Bean

 @ConditionalOnBean(LoadBalancerClient.class)

 public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client) {

  return new LoadBalancerClientFilter(client);

 }

}

之后便是我們的GatewayAutoConfiguration正式加載了,這個(gè)里面定義了非常多的內(nèi)容,我們大部分用到的過(guò)濾器,過(guò)濾器工廠(chǎng)都是在這里構(gòu)建的。包括之前的gatewayControllerEndpoint也是在這里注入容器中的。這個(gè)類(lèi)的定義很長(zhǎng),我就不再這里都放了,列舉幾個(gè)。


@Configuration

//開(kāi)啟網(wǎng)關(guān),不寫(xiě)默認(rèn)為true

@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)

@EnableConfigurationProperties

@AutoConfigureBefore(HttpHandlerAutoConfiguration.class)

@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})

@ConditionalOnClass(DispatcherHandler.class)

public class GatewayAutoConfiguration {

 @Configuration

 @ConditionalOnClass(HttpClient.class)

 protected static class NettyConfiguration {

  @Bean

  @ConditionalOnMissingBean

  public HttpClient httpClient(@Qualifier("nettyClientOptions") Consumer<? super HttpClientOptions.Builder> options) {

   return HttpClient.create(options);

  }

  ... //還有很多 這里不列舉了。我們都知道spring cloud 是基于Netty實(shí)現(xiàn)的,這里他這個(gè)靜態(tài)內(nèi)部類(lèi)就是初始化netty需要的東西。

}

//初始化了加載配置文件的對(duì)象,建立route

@Bean

 public GatewayProperties gatewayProperties() {

  return new GatewayProperties();

 }

//初始化請(qǐng)求轉(zhuǎn)發(fā) 過(guò)濾器

@Bean

 @ConditionalOnProperty(name = "spring.cloud.gateway.forwarded.enabled", matchIfMissing = true)

 public ForwardedHeadersFilter forwardedHeadersFilter() {

  return new ForwardedHeadersFilter();

 }

 //最后初始化gatewayControllerEndpoint 這里注意只有引入spring-boot-starter-actuator他才會(huì)加載

@Configuration

 @ConditionalOnClass(Health.class)

 protected static class GatewayActuatorConfiguration {

  @Bean

  @ConditionalOnEnabledEndpoint

  public GatewayControllerEndpoint gatewayControllerEndpoint(RouteDefinitionLocator routeDefinitionLocator, List<GlobalFilter> globalFilters,

                List<GatewayFilterFactory> GatewayFilters, RouteDefinitionWriter routeDefinitionWriter,

                RouteLocator routeLocator) {

   return new GatewayControllerEndpoint(routeDefinitionLocator, globalFilters, GatewayFilters, routeDefinitionWriter, routeLocator);

  }

 }

這里注意我們通過(guò)注冊(cè)中心發(fā)現(xiàn)的路由不是在config包下定義的而是在discovery包下GatewayDiscoveryClientAutoConfiguration實(shí)現(xiàn)了從注冊(cè)中心發(fā)現(xiàn)內(nèi)容


@Configuration

@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)

@AutoConfigureBefore(GatewayAutoConfiguration.class)

@ConditionalOnClass({DispatcherHandler.class, DiscoveryClient.class})

@EnableConfigurationProperties

public class GatewayDiscoveryClientAutoConfiguration {

 @Bean

 @ConditionalOnBean(DiscoveryClient.class)

 @ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")

 public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(

   DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {

  return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);

 }

 @Bean public DiscoveryLocatorProperties discoveryLocatorProperties() {

  DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();

  properties.setPredicates(initPredicates());

  properties.setFilters(initFilters());

  return properties;

 }

}

這些注冊(cè)完畢后,我們的配置文件就開(kāi)始讀取了,

這兩個(gè)中定義了我們配置文件的讀取規(guī)則,其中DiscoveryLocatorProperties都有默認(rèn)的值,我們可以不用關(guān)心。GatewayProperties比較重要


@ConfigurationProperties("spring.cloud.gateway")

@Validated

public class GatewayProperties {

 @NotNull

 @Valid

 private List<RouteDefinition> routes = new ArrayList<>();

 /**

  * List of filter definitions that are applied to every route.

  */

 private List<FilterDefinition> defaultFilters = new ArrayList<>();

 private List<MediaType> streamingMediaTypes = Arrays.asList(MediaType.TEXT_EVENT_STREAM,

   MediaType.APPLICATION_STREAM_JSON);

}

這里就定義了我們配置文件里的所有路由信息,讀取完成后,接下來(lái)就要變成真正的路由信息了。

這里就要說(shuō)一下 RouteLocator接口,這個(gè)接口提供了一個(gè)getRoutes()方法返回一個(gè)Flux<Route>所以,他的實(shí)現(xiàn)中就定義了讀取配置文件轉(zhuǎn)成路由的關(guān)鍵。


public interface RouteLocator {

 Flux<Route> getRoutes();

}

我們先看從配置文件中加載的路由信息也就是他的實(shí)現(xiàn)RouteDefinitionRouteLocator


public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {

 protected final Log logger = LogFactory.getLog(getClass());

 private final RouteDefinitionLocator routeDefinitionLocator;

 private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();

 private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();

 private final GatewayProperties gatewayProperties;

 private final SpelExpressionParser parser = new SpelExpressionParser();

 private BeanFactory beanFactory;

 private ApplicationEventPublisher publisher;

 public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,

            List<RoutePredicateFactory> predicates,

            List<GatewayFilterFactory> gatewayFilterFactories,

            GatewayProperties gatewayProperties) {

  this.routeDefinitionLocator = routeDefinitionLocator;

  initFactories(predicates);

  gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory));

  this.gatewayProperties = gatewayProperties;

 }

  ...

}

我們定位到他的構(gòu)造方法,debug啟動(dòng)

發(fā)現(xiàn)這個(gè)類(lèi)的一個(gè)屬性routeDefinitionLocator中已經(jīng)定義了我們的路由,只不過(guò)是代理對(duì)象。 這個(gè)屬性所對(duì)應(yīng)的接口RouteDefinitionLocator有很多種實(shí)現(xiàn)

我們現(xiàn)在看PropertiesRouteDefinitionLocator,這里


public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {

 private final GatewayProperties properties;

 public PropertiesRouteDefinitionLocator(GatewayProperties properties) {

  this.properties = properties;

 }

 @Override

 public Flux<RouteDefinition> getRouteDefinitions() {

  return Flux.fromIterable(this.properties.getRoutes());

 }

}

他的構(gòu)造方法讀取了GatewayProperties ,所以到這里我們的路由就已經(jīng)存在了。通過(guò)他的getRoutes()方法,我們就能方便的取出所有定義的路由信息了

而他的getRoutes()方法又是這樣定義的


@Override

 public Flux<Route> getRoutes() {

  return this.routeDefinitionLocator.getRouteDefinitions()

    .map(this::convertToRoute)

    //TODO: error handling

    .map(route -> {

     if (logger.isDebugEnabled()) {

      logger.debug("RouteDefinition matched: " + route.getId());

     }

     return route;

    });

 }

routeDefinitionLocator.getRouteDefinitions()返回從配置文件中讀取的路由轉(zhuǎn)換成 Flux<RouteDefinition> 再通過(guò)convertToRoute轉(zhuǎn)換成路由對(duì)象的數(shù)組,封裝成Flux()至此,配置文件中讀取路由信息就結(jié)束了。

接下來(lái)我們來(lái)解釋通過(guò)注冊(cè)中心的方式接受的路由信息。這里由一個(gè)叫DiscoveryClientRouteDefinitionLocator的類(lèi)來(lái)實(shí)現(xiàn),他同樣實(shí)現(xiàn)了RouteDefinitionLocator接口,能夠返回Flux<RouteDefinition>


public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {

 private final DiscoveryClient discoveryClient;

 private final DiscoveryLocatorProperties properties;

 private final String routeIdPrefix;

 public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {

// 服務(wù)發(fā)現(xiàn)信息 

  this.discoveryClient = discoveryClient;

 //這個(gè)配置是前面說(shuō)過(guò)的DiscoveryLocatorProperties

  this.properties = properties;

  if (StringUtils.hasText(properties.getRouteIdPrefix())) {

   this.routeIdPrefix = properties.getRouteIdPrefix();

  } else {

   this.routeIdPrefix = this.discoveryClient.getClass().getSimpleName() + "_";

  }

 }

 //這個(gè)是核心

 @Override

 public Flux<RouteDefinition> getRouteDefinitions() {

  SimpleEvaluationContext evalCtxt = SimpleEvaluationContext

    .forReadOnlyDataBinding()

    .withInstanceMethods()

    .build();

  SpelExpressionParser parser = new SpelExpressionParser();

  Expression includeExpr = parser.parseExpression(properties.getIncludeExpression());

  Expression urlExpr = parser.parseExpression(properties.getUrlExpression());

  return Flux.fromIterable(discoveryClient.getServices())

    .map(discoveryClient::getInstances)

    .filter(instances -> !instances.isEmpty())

    .map(instances -> instances.get(0))

    .filter(instance -> {

     Boolean include = includeExpr.getValue(evalCtxt, instance, Boolean.class);

     if (include == null) {

      return false;

     }

     return include;

    })

    .map(instance -> {

     String serviceId = instance.getServiceId();

                    RouteDefinition routeDefinition = new RouteDefinition();

                    routeDefinition.setId(this.routeIdPrefix + serviceId);

     String uri = urlExpr.getValue(evalCtxt, instance, String.class);

     routeDefinition.setUri(URI.create(uri));

     final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);

     for (PredicateDefinition original : this.properties.getPredicates()) {

      PredicateDefinition predicate = new PredicateDefinition();

      predicate.setName(original.getName());

      for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {

       String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);

       predicate.addArg(entry.getKey(), value);

      }

      routeDefinition.getPredicates().add(predicate);

     }

                    for (FilterDefinition original : this.properties.getFilters()) {

                     FilterDefinition filter = new FilterDefinition();

                     filter.setName(original.getName());

      for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {

       String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);

       filter.addArg(entry.getKey(), value);

      }

      routeDefinition.getFilters().add(filter);

     }

                    return routeDefinition;

    });

 }

當(dāng)我們調(diào)用到getRouteDefinitions的時(shí)候,他通過(guò)discoveryClient轉(zhuǎn)換出了服務(wù)發(fā)現(xiàn)中心的服務(wù)路由

注意這里他沒(méi)有被轉(zhuǎn)換為Flux<Route>而是保留為Flux<RouteDefinition> 這是一個(gè)坑,我們前期很多時(shí)候去找Route發(fā)現(xiàn)根本就沒(méi)有從服務(wù)注冊(cè)中心拉下來(lái)的,現(xiàn)在才知道,他壓根就沒(méi)去轉(zhuǎn)。

到這里,我們的路由信息就加載完畢了,網(wǎng)關(guān)也就啟動(dòng)完成了,之后就是發(fā)送請(qǐng)求過(guò)濾器發(fā)功的時(shí)候了。

我們第一個(gè)關(guān)注的類(lèi)就是DispatcherHandler,這個(gè)類(lèi)提供的handle()方法,封裝了我們之后所有的handlerMappings


public class DispatcherHandler implements WebHandler, ApplicationContextAware {

 ...

 @Override

 public Mono<Void> handle(ServerWebExchange exchange) {

  if (logger.isDebugEnabled()) {

   ServerHttpRequest request = exchange.getRequest();

   logger.debug("Processing " + request.getMethodValue() + " request for [" + request.getURI() + "]");

  }

  if (this.handlerMappings == null) {

   return Mono.error(HANDLER_NOT_FOUND_EXCEPTION);

  }

  return Flux.fromIterable(this.handlerMappings)

    .concatMap(mapping -> mapping.getHandler(exchange))

    .next()

    .switchIfEmpty(Mono.error(HANDLER_NOT_FOUND_EXCEPTION))

    .flatMap(handler -> invokeHandler(exchange, handler))

    .flatMap(result -> handleResult(exchange, result));

 }

...

}

這里會(huì)真正的調(diào)用我們前面注冊(cè)的route 的那些 getRouteDefinitions()方法,具體的我也沒(méi)看明白,大概就是封裝了這些Mapping進(jìn)入之后的過(guò)濾鏈中

之后就到了FilteringWebHandler類(lèi),他的handler方法封裝了過(guò)濾器進(jìn)去


@Override

 public Mono<Void> handle(ServerWebExchange exchange) {

  //h獲取到當(dāng)前訪(fǎng)問(wèn)的路由對(duì)象

  Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);

  //獲取當(dāng)前已經(jīng)存在的 過(guò)濾器 也就是配置的默認(rèn)過(guò)濾器

  List<GatewayFilter> gatewayFilters = route.getFilters();

 //獲取全局過(guò)濾器

  List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);

  combined.addAll(gatewayFilters);

  //按order排序

  AnnotationAwareOrderComparator.sort(combined);

  logger.debug("Sorted gatewayFilterFactories: "+ combined);

  return new DefaultGatewayFilterChain(combined).filter(exchange);

 }

這里的全局過(guò)濾器包括除了我標(biāo)記的兩個(gè)以外的所有過(guò)濾器,之后再介紹

最后按照order排序后的過(guò)濾器順序?yàn)?/p>

之后就真正的開(kāi)始進(jìn)入過(guò)濾鏈了 排除掉我們自己實(shí)現(xiàn)的過(guò)濾器 第一個(gè)進(jìn)入的是AdaptCachedBodyGlobalFilter

他用來(lái)緩存我們的requestBody


@Override

 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

  Flux<DataBuffer> body = exchange.getAttributeOrDefault(CACHED_REQUEST_BODY_KEY, null);

  if (body != null) {

  //封裝  ServerHttpRequest 使得requestBody 能夠復(fù)寫(xiě)

   ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {

    @Override

    public Flux<DataBuffer> getBody() {

     return body;

    }

   };

   return chain.filter(exchange.mutate().request(decorator).build());

  }

  return chain.filter(exchange);

 }

然后進(jìn)入的是NettyWriteResponseFilter 這個(gè)過(guò)濾器實(shí)現(xiàn)的是返回response 也就是請(qǐng)求完成后返回的對(duì)象。他這里巧妙的使用then()


@Override

 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

  // NOTICE: nothing in "pre" filter stage as CLIENT_RESPONSE_ATTR is not added

  // then 當(dāng)filter完成后執(zhí)行,也就是過(guò)濾鏈執(zhí)行完畢,返回結(jié)果的時(shí)候

  return chain.filter(exchange).then(Mono.defer(() -> {

   HttpClientResponse clientResponse = exchange.getAttribute(CLIENT_RESPONSE_ATTR);

   if (clientResponse == null) {

    return Mono.empty();

   }

   log.trace("NettyWriteResponseFilter start");

   ServerHttpResponse response = exchange.getResponse();

   NettyDataBufferFactory factory = (NettyDataBufferFactory) response.bufferFactory();

   //TODO: what if it's not netty

   final Flux<NettyDataBuffer> body = clientResponse.receive()

     .retain() //TODO: needed?

     .map(factory::wrap);

   MediaType contentType = response.getHeaders().getContentType();

   return (isStreamingMediaType(contentType) ?

     response.writeAndFlushWith(body.map(Flux::just)) : response.writeWith(body));

  }));

 }

后面就是返回結(jié)果了 沒(méi)什么可說(shuō)的。之后的過(guò)濾器我就不一個(gè)一個(gè)說(shuō)了,講講比較重要的。

RouteToRequestUrlFilter我在注釋上標(biāo)明了,當(dāng)前uri的值


@Override

 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

  Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);

  if (route == null) {

   return chain.filter(exchange);

  }

  log.trace("RouteToRequestUrlFilter start");\

  // http://localhost:8804/cydOrganization/getOrgTreeList

  URI uri = exchange.getRequest().getURI();

  boolean encoded = containsEncodedParts(uri);

  // lb://BASE-API-WEB

  URI routeUri = route.getUri();

  if (hasAnotherScheme(routeUri)) {

   // this is a special url, save scheme to special attribute

   // replace routeUri with schemeSpecificPart

   exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR, routeUri.getScheme());

   routeUri = URI.create(routeUri.getSchemeSpecificPart());

  }

  // lb://BASE-API-WEB:8804/cydOrganization/getOrgTreeList

  URI requestUrl = UriComponentsBuilder.fromUri(uri)

    .uri(routeUri)

    .build(encoded)

    .toUri();

  exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);

  return chain.filter(exchange);

 }

最后他將url拼裝成了我們需要的,能夠做服務(wù)發(fā)現(xiàn)的url,這時(shí)他就會(huì)進(jìn)入LoadBalancerClientFilter 同樣我標(biāo)注url的變化


@Override

 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

  // lb://BASE-API-WEB:8804/cydOrganization/getOrgTreeList 剛才我們封裝完畢的url

  URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);

  String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);

  if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {

   return chain.filter(exchange);

  }

  //preserve the original url

  addOriginalRequestUrl(exchange, url);

  log.trace("LoadBalancerClientFilter url before: " + url);

  // RibbonServer{serviceId='BASE-API-WEB', server=192.168.47.1:12993, secure=false, metadata={}}

  final ServiceInstance instance = loadBalancer.choose(url.getHost());

  if (instance == null) {

   throw new NotFoundException("Unable to find instance for " + url.getHost());

  }

  // http://localhost:8804/cydOrganization/getOrgTreeList

  URI uri = exchange.getRequest().getURI();

  // if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,

  // if the loadbalancer doesn't provide one.

  String overrideScheme = null;

  if (schemePrefix != null) {

   overrideScheme = url.getScheme();

  }

  // http://192.168.47.1:12993/cydOrganization/getOrgTreeList 到這時(shí) 這個(gè)地址已經(jīng)是正確的訪(fǎng)問(wèn)地址了。

  URI requestUrl = loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri);

  log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);

  exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);

  return chain.filter(exchange);

 }

最后進(jìn)入NettyRoutingFilter 這個(gè)filter真正做請(qǐng)求的發(fā)送,他使用HttpClient進(jìn)行請(qǐng)求的發(fā)送。這個(gè)類(lèi)不是很長(zhǎng) 我就都拷過(guò)來(lái)


public class NettyRoutingFilter implements GlobalFilter, Ordered {

 private final HttpClient httpClient;

 private final ObjectProvider<List<HttpHeadersFilter>> headersFilters;

 public NettyRoutingFilter(HttpClient httpClient,

   ObjectProvider<List<HttpHeadersFilter>> headersFilters) {

  this.httpClient = httpClient;

  this.headersFilters = headersFilters;

 }

 @Override

 public int getOrder() {

  return Ordered.LOWEST_PRECEDENCE;

 }

 @Override

 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { 

// http://192.168.47.1:12993/cydOrganization/getOrgTreeList 我們剛才處理好的url

  URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);

  String scheme = requestUrl.getScheme(); 

  //必須是 http 或者 https 和沒(méi)有被路由

  if (isAlreadyRouted(exchange) || (!"http".equals(scheme) && !"https".equals(scheme))) {

   return chain.filter(exchange);

  }

 // 設(shè)置為已經(jīng)路由,防止請(qǐng)求重復(fù)路由

  setAlreadyRouted(exchange);

  ServerHttpRequest request = exchange.getRequest();

  final HttpMethod method = HttpMethod.valueOf(request.getMethod().toString());

  final String url = requestUrl.toString();

  HttpHeaders filtered = filterRequest(this.headersFilters.getIfAvailable(),

    exchange);

  final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();

  filtered.forEach(httpHeaders::set);

  String transferEncoding = request.getHeaders().getFirst(HttpHeaders.TRANSFER_ENCODING);

  boolean chunkedTransfer = "chunked".equalsIgnoreCase(transferEncoding);

  boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);

 //到這里發(fā)送http請(qǐng)求

  return this.httpClient.request(method, url, req -> {

   final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach)

     .headers(httpHeaders)

     .chunkedTransfer(chunkedTransfer)

     .failOnServerError(false)

     .failOnClientError(false);

   if (preserveHost) {

    String host = request.getHeaders().getFirst(HttpHeaders.HOST);

    proxyRequest.header(HttpHeaders.HOST, host);

   }

   return proxyRequest.sendHeaders() //I shouldn't need this

     .send(request.getBody().map(dataBuffer ->

       ((NettyDataBuffer)dataBuffer).getNativeBuffer()));

  }).doOnNext(res -> { //接受請(qǐng)求返回結(jié)果

   ServerHttpResponse response = exchange.getResponse();

   // put headers and status so filters can modify the response

   HttpHeaders headers = new HttpHeaders();

   res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue()));

// 這里可能導(dǎo)致空指針  是我用的版本的bug  SR2版本已經(jīng)解決 

   exchange.getAttributes().put("original_response_content_type", headers.getContentType());

   HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(

     this.headersFilters.getIfAvailable(), headers, exchange, Type.RESPONSE);

   response.getHeaders().putAll(filteredResponseHeaders);

   HttpStatus status = HttpStatus.resolve(res.status().code());

   if (status != null) {

    response.setStatusCode(status);

   } else if (response instanceof AbstractServerHttpResponse) {

    // https://jira.spring.io/browse/SPR-16748

    ((AbstractServerHttpResponse) response).setStatusCodeValue(res.status().code());

   } else {

    throw new IllegalStateException("Unable to set status code on response: " +res.status().code()+", "+response.getClass());

   }

   // Defer committing the response until all route filters have run

   // Put client response as ServerWebExchange attribute and write response later NettyWriteResponseFilter

   exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res);

  }).then(chain.filter(exchange));

 }

}

當(dāng)這步執(zhí)行完之后,可以理解為先前的NettyWriteResponseFilter 的then()開(kāi)始執(zhí)行,最終返回請(qǐng)求結(jié)果。至此spring cloud gateway 的啟動(dòng)流程和訪(fǎng)問(wèn)流程就結(jié)束了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容