Spring Cloud Gateway 开发随笔

Gateway 常用配置

CORS 跨域配置

YML 配置方式

1
2
3
4
5
6
7
8
9
10
11
spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOriginPatterns: "*" # 注意这个设置只对 Spring Boot 2.4+ 有效,低版本需要使用 allowedOrigins: "*" 属性
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
exposedHeaders: "Content-Disposition,Content-Type,Cache-Control"

若添加上述配置后,前端页面出现 The 'Access-Control-Allow-Origin' header contains multiple values 'http://127.0.0.1:8010, http://127.0.0.1:8010', but only one is allowed. 这样的错误,则需要添加以下配置来去掉重复的 HTTP Header

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
cloud:
gateway:
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin Access-Control-Allow-Headersa
globalcors:
corsConfigurations:
'[/**]':
allowedOriginPatterns: "*" # 注意这个设置只对 Spring Boot 2.4+ 有效,低版本需要使用 allowedOrigins: "*" 属性
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
exposedHeaders: "Content-Disposition,Content-Type,Cache-Control"

Java 配置方式

  • 通过 Java 代码配置跨域过滤器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

@Configuration
public class GlobalCorsConfig {

@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://127.0.0.1:8010"); // 允许的域名
config.addAllowedHeader("*"); // 允许的请求头
config.addAllowedMethod("*"); // 允许的请求方法
config.setAllowCredentials(true); // 是否允许发送Cookie
config.addExposedHeader("Content-Disposition");
config.addExposedHeader("Content-Type");
config.addExposedHeader("Cache-Control");

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}

}

路由超时时间配置

路由超时配置可以为所有路由配置 HTTP 超时(响应和连接),并为每个特定路由覆盖 HTTP 超时。

全局路由的超时时间配置

配置全局 HTTP 超时(响应和连接),需要使用以下两个参数:

  • connect-timeout:必须以毫秒为单位指定连接超时时间
  • response-timeout:必须指定为 java.time.Duration
1
2
3
4
5
6
spring:
cloud:
gateway:
httpclient:
connect-timeout: 1000
response-timeout: 5s

每个路由的超时时间配置

可以通过路由的 metadata 以下两个参数配置每个路由的超时时间:

  • connect-timeout:必须以毫秒为单位指定连接超时时间
  • response-timeout:必须以毫秒为单位指定响应超时时间
1
2
3
4
5
6
7
8
9
- id: per_route_timeouts
uri: https://example.org
predicates:
- name: Path
args:
pattern: /delay/{timeout}
metadata:
response-timeout: 1000
connect-timeout: 1000

使用 Java DSL 为每个路由配置超时时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import static org.springframework.cloud.gateway.support.RouteMetadataUtils.CONNECT_TIMEOUT_ATTR;
import static org.springframework.cloud.gateway.support.RouteMetadataUtils.RESPONSE_TIMEOUT_ATTR;

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeBuilder){
return routeBuilder.routes()
.route("test1", r -> {
return r.host("*.somehost.org").and().path("/somepath")
.filters(f -> f.addRequestHeader("header1", "header-value-1"))
.uri("http://someuri")
.metadata(RESPONSE_TIMEOUT_ATTR, 1000)
.metadata(CONNECT_TIMEOUT_ATTR, 1000);
})
.build();
}

常见使用问题

整合 Nacos 出现 503 错误

  • 版本说明
Spring BootSpring CloudSpring Cloud Alibaba
2.6.32021.0.12021.0.1.0
3.2.02023.0.02022.0.0.0
  • Gateway 的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.56.103:8848
gateway:
routes:
- id: tmall-product
uri: lb://tmall-product
predicates:
- Path=/api/**
filters:
- RewritePath=/api(?<segment>/?.*), /tmall-product/$\{segment}
  • 问题描述

Gateway 使用 Nacos 作为服务注册中心,基于上述的配置内容,当通过 uri: http://127.0.0.1:9090 去直接调用 tmall-product 服务时,是可以正常调用的,但是使用 uri: lb://tmall-product 时就无法调用服务,Gateway 返回 503 错误码(如下图)。

  • 解决方案

造成 Gateway 负载均衡失效的原因是,从 Spring Cloud 2020 版本开始,Spring Cloud 弃用了 Ribbon,使用 Spring Cloud Loadbalancer 作为客户端的负载均衡组件;因此 Spring Cloud Alibaba 在 2020 及之后版本的 Nacos 中也移除了 Ribbon 的依赖,最终导致 Gateway 无法通过 lb:// 路由到指定的服务,继而出现了 503 错误码。解决方案是引入 Spring Cloud Loadbalancer 的 Maven 坐标即可:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>

context-path 导致服务调用失败

  • 微服务应用配置了 context-path,并且指定 IP 将微服务注册到 Nacos 注册中心
1
2
3
4
5
6
7
8
9
10
11
server:
port: 8004
servlet:
context-path: /

spring:
cloud:
nacos:
discovery:
ip: 192.168.1.140
server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
  • 当外部通过 Gateway 调用微服务应用的接口时,会返回以下错误信息:
1
2
3
4
5
{
"code": 1,
"msg": "null: /192.168.1.140:8004",
"data": null
}
  • 解决办法有两种:
    • (1) 去掉 context-path 的配置
      1
      2
      3
      4
      5
      6
      7
      8
      9
      server:
      port: 8004

      spring:
      cloud:
      nacos:
      discovery:
      ip: 192.168.1.140
      server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
    • (2) 将微服务注册进 Nacos 时,指定元数据(metadata
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      server:
      port: 8004
      servlet:
      context-path: /

      spring:
      cloud:
      nacos:
      discovery:
      ip: 192.168.1.140
      server-addr: ${NACOS_HOST:127.0.0.1}:${NACOS_PORT:8848}
      metadata:
      management.context-path: ${server.servlet.context-path}
      # management.context-path: ${server.servlet.context-path}/actuator