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

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

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


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

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

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

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

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

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

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

support是工具包等脐瑰。

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

config包

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

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

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

這里的GatewayClassPathWarningAutoConfiguration這個(gè)類,就指明了我們需要什么不需要什么结啼,他加載于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ù)載均衡的過濾器實(shí)現(xiàn)的加載,他將LoadBalancerClientFilter 注入到了容器中属铁,這個(gè)過濾器后面再說眠寿。


@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)容焦蘑,我們大部分用到的過濾器盯拱,過濾器工廠都是在這里構(gòu)建的。包括之前的gatewayControllerEndpoint也是在這里注入容器中的例嘱。這個(gè)類的定義很長(zhǎng)狡逢,我就不再這里都放了,列舉幾個(gè)拼卵。


@Configuration

//開啟網(wǎng)關(guān)奢浑,不寫默認(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)部類就是初始化netty需要的東西雀彼。

}

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

@Bean

 public GatewayProperties gatewayProperties() {

  return new GatewayProperties();

 }

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

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

  }

 }

這里注意我們通過注冊(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è)完畢后,我們的配置文件就開始讀取了徊哑,

這兩個(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);

}

這里就定義了我們配置文件里的所有路由信息水泉,讀取完成后,接下來就要變成真正的路由信息了窒盐。

這里就要說一下 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è)類的一個(gè)屬性routeDefinitionLocator中已經(jīng)定義了我們的路由,只不過是代理對(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)存在了嗽交。通過他的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> 再通過convertToRoute轉(zhuǎn)換成路由對(duì)象的數(shù)組,封裝成Flux()至此夫壁,配置文件中讀取路由信息就結(jié)束了拾枣。

接下來我們來解釋通過注冊(cè)中心的方式接受的路由信息。這里由一個(gè)叫DiscoveryClientRouteDefinitionLocator的類來實(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è)配置是前面說過的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í)候,他通過discoveryClient轉(zhuǎn)換出了服務(wù)發(fā)現(xiàn)中心的服務(wù)路由

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

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

我們第一個(gè)關(guān)注的類就是DispatcherHandler,這個(gè)類提供的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()方法黔寇,具體的我也沒看明白,大概就是封裝了這些Mapping進(jìn)入之后的過濾鏈中

之后就到了FilteringWebHandler類斩萌,他的handler方法封裝了過濾器進(jìn)去


@Override

 public Mono<Void> handle(ServerWebExchange exchange) {

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

  Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);

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

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

 //獲取全局過濾器

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

 }

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

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

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

他用來緩存我們的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ù)寫

   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è)過濾器實(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í)行,也就是過濾鏈執(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é)果了 沒什么可說的霎苗。之后的過濾器我就不一個(gè)一個(gè)說了,講講比較重要的榛做。

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)是正確的訪問地址了厘擂。

  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è)類不是很長(zhǎng) 我就都拷過來


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 和沒有被路由

  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()開始執(zhí)行避凝,最終返回請(qǐng)求結(jié)果舞萄。至此spring cloud gateway 的啟動(dòng)流程和訪問流程就結(jié)束了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末管削,一起剝皮案震驚了整個(gè)濱河市倒脓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌佩谣,老刑警劉巖把还,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件实蓬,死亡現(xiàn)場(chǎng)離奇詭異茸俭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)安皱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門调鬓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人酌伊,你說我怎么就攤上這事腾窝。” “怎么了居砖?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵虹脯,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我奏候,道長(zhǎng)循集,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任蔗草,我火速辦了婚禮咒彤,結(jié)果婚禮上疆柔,老公的妹妹穿的比我還像新娘。我一直安慰自己镶柱,他們只是感情好旷档,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著歇拆,像睡著了一般鞋屈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上故觅,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天谐区,我揣著相機(jī)與錄音,去河邊找鬼逻卖。 笑死宋列,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的评也。 我是一名探鬼主播炼杖,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼盗迟!你這毒婦竟也來了坤邪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤罚缕,失蹤者是張志新(化名)和其女友劉穎艇纺,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體邮弹,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡黔衡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了腌乡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盟劫。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖与纽,靈堂內(nèi)的尸體忽然破棺而出侣签,到底是詐尸還是另有隱情,我是刑警寧澤急迂,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布影所,位于F島的核電站,受9級(jí)特大地震影響僚碎,放射性物質(zhì)發(fā)生泄漏猴娩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胀溺。 院中可真熱鬧裂七,春花似錦、人聲如沸仓坞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽无埃。三九已至徙瓶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嫉称,已是汗流浹背侦镇。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留织阅,地道東北人壳繁。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像荔棉,于是被迫代替她去往敵國(guó)和親闹炉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354