spring cloud gateway 启动流程及原理分析

spring cloud gateway 作为新一代的微服务网关已经发布了一段时间,我从7月份开始使用到现在已经4个月了。但是我一直处于一种只会使用,原理一知半解的水平。我们小组作为公司微服务产品的实践者,我自己作为组中一员完成了spring cloud gateway的开发,也解决了很多棘手的问题,却对它的原理和启动流程一知半解,好几次就是因为不了解它的启动流程,导致开发受挫,进度缓慢。现在正值闲时,正好看一下相关的源码,理解他的启动流程。本文基于spring cloud Finchley.RELEASE版本,最新的SR2版本一些内容有改变,但总体改变不大。

首先是网关的包结构


其中actuate中定义了一个叫GatewayControllerEndpoint的类,这个类提供一些对外的接口,可以获取网关的一些信息,比如路由的信息,改变路由地址等等。

config中定义了一些启动时去加载的类,配置路由信息和读取你的配置文件就在这里完成。

discovery中定义了注册中心相关的内容,包括注册中心的路由等。

event定义了一些事件他们都继承自ApplicationEvent,对事件发布不了解的可以去看看spring的代码。

filter中定义了spring cloud gateway实现的一些过滤器。

handler中定义了很多Predicate相关的Factory

route就是我们路由的相关

support是工具包等。

下面就是gateway启动流程的分析了

config包

网关启动第一步加载的就是去加载config包下的几个类。

这几个类就定义了网关需要加载的配置项。

在我第一次做网关开发的时候我引入了spring-boot-starter-web的依赖,这样是会报错的,因为gateway是基于spring-webflux开发的,他依赖的DispatcherHandler就和我们web里的DispatcherServlet一样的功能。

这里的GatewayClassPathWarningAutoConfiguration这个类,就指明了我们需要什么不需要什么,他加载于GatewayAutoConfiguration之前,如果DispatcherServlet存在,就会给与警告,同样的DispatcherHandler不存在也会警告。


@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这个是gateway负载均衡的过滤器实现的加载,他将LoadBalancerClientFilter 注入到了容器中,这个过滤器后面再说。


@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正式加载了,这个里面定义了非常多的内容,我们大部分用到的过滤器,过滤器工厂都是在这里构建的。包括之前的gatewayControllerEndpoint也是在这里注入容器中的。这个类的定义很长,我就不再这里都放了,列举几个。


@Configuration

//开启网关,不写默认为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实现的,这里他这个静态内部类就是初始化netty需要的东西。

}

//初始化了加载配置文件的对象,建立route

@Bean

 public GatewayProperties gatewayProperties() {

  return new GatewayProperties();

 }

//初始化请求转发 过滤器

@Bean

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

 public ForwardedHeadersFilter forwardedHeadersFilter() {

  return new ForwardedHeadersFilter();

 }

 //最后初始化gatewayControllerEndpoint 这里注意只有引入spring-boot-starter-actuator他才会加载

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

  }

 }

这里注意我们通过注册中心发现的路由不是在config包下定义的而是在discovery包下GatewayDiscoveryClientAutoConfiguration实现了从注册中心发现内容


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

 }

}

这些注册完毕后,我们的配置文件就开始读取了,

这两个中定义了我们配置文件的读取规则,其中DiscoveryLocatorProperties都有默认的值,我们可以不用关心。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接口,这个接口提供了一个getRoutes()方法返回一个Flux<Route>所以,他的实现中就定义了读取配置文件转成路由的关键。


public interface RouteLocator {

 Flux<Route> getRoutes();

}

我们先看从配置文件中加载的路由信息也就是他的实现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;

 }

  ...

}

我们定位到他的构造方法,debug启动

发现这个类的一个属性routeDefinitionLocator中已经定义了我们的路由,只不过是代理对象。 这个属性所对应的接口RouteDefinitionLocator有很多种实现

我们现在看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());

 }

}

他的构造方法读取了GatewayProperties ,所以到这里我们的路由就已经存在了。通过他的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()返回从配置文件中读取的路由转换成 Flux<RouteDefinition> 再通过convertToRoute转换成路由对象的数组,封装成Flux()至此,配置文件中读取路由信息就结束了。

接下来我们来解释通过注册中心的方式接受的路由信息。这里由一个叫DiscoveryClientRouteDefinitionLocator的类来实现,他同样实现了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) {

// 服务发现信息 

  this.discoveryClient = discoveryClient;

 //这个配置是前面说过的DiscoveryLocatorProperties

  this.properties = properties;

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

   this.routeIdPrefix = properties.getRouteIdPrefix();

  } else {

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

  }

 }

 //这个是核心

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

    });

 }

当我们调用到getRouteDefinitions的时候,他通过discoveryClient转换出了服务发现中心的服务路由

注意这里他没有被转换为Flux<Route>而是保留为Flux<RouteDefinition> 这是一个坑,我们前期很多时候去找Route发现根本就没有从服务注册中心拉下来的,现在才知道,他压根就没去转。

到这里,我们的路由信息就加载完毕了,网关也就启动完成了,之后就是发送请求过滤器发功的时候了。

我们第一个关注的类就是DispatcherHandler,这个类提供的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));

 }

...

}

这里会真正的调用我们前面注册的route 的那些 getRouteDefinitions()方法,具体的我也没看明白,大概就是封装了这些Mapping进入之后的过滤链中

之后就到了FilteringWebHandler类,他的handler方法封装了过滤器进去


@Override

 public Mono<Void> handle(ServerWebExchange exchange) {

  //h获取到当前访问的路由对象

  Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);

  //获取当前已经存在的 过滤器 也就是配置的默认过滤器

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

 }

这里的全局过滤器包括除了我标记的两个以外的所有过滤器,之后再介绍

最后按照order排序后的过滤器顺序为

之后就真正的开始进入过滤链了 排除掉我们自己实现的过滤器 第一个进入的是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 能够复写

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

 }

然后进入的是NettyWriteResponseFilter 这个过滤器实现的是返回response 也就是请求完成后返回的对象。他这里巧妙的使用then()


@Override

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

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

  // then 当filter完成后执行,也就是过滤链执行完毕,返回结果的时候

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

  }));

 }

后面就是返回结果了 没什么可说的。之后的过滤器我就不一个一个说了,讲讲比较重要的。

RouteToRequestUrlFilter我在注释上标明了,当前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拼装成了我们需要的,能够做服务发现的url,这时他就会进入LoadBalancerClientFilter 同样我标注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 到这时 这个地址已经是正确的访问地址了。

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

 }

最后进入NettyRoutingFilter 这个filter真正做请求的发送,他使用HttpClient进行请求的发送。这个类不是很长 我就都拷过来


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

  }

 // 设置为已经路由,防止请求重复路由

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

 //到这里发送http请求

  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 -> { //接受请求返回结果

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

// 这里可能导致空指针  是我用的版本的bug  SR2版本已经解决 

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

 }

}

当这步执行完之后,可以理解为先前的NettyWriteResponseFilter 的then()开始执行,最终返回请求结果。至此spring cloud gateway 的启动流程和访问流程就结束了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343