Get Ready for Gateway

本文详细介绍了Spring Cloud Gateway的功能和架构,包括其作为微服务API网关的角色,提供了协议适配、转发、安全、监控及弹性支持。文章还探讨了Gateway的内部组件,如Predicate和Filter,以及如何通过配置实现各种功能,如动态路由、请求头添加和限流。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



什么是Gateway

  Gateway是微服务的API网关。它的主要作用是网关的管理,另外还提供协议适配、协议转发、安全、监控、弹性等功能。



为什么引入网关

  没有网关之前:
在这里插入图片描述
使用方需要记住需要调用哪些服务,一旦后端的服务发生变化,需要该很多的配置。

  有了网关之后:
在这里插入图片描述
清晰明了。

当某些应用较多时,甚至可以是gateway of gateway
在这里插入图片描述



Gateway的组成

  由Netty写的上游服务端和下游客户端。协议的适配和转发就是由两端的Netty来完成的。中间是许多Route配置规则,每个Route包含Predicate和Filter。
在这里插入图片描述
  每个Route放大之后:
在这里插入图片描述
Predicate进行规则的匹配,匹配上了就交由对应的Filter去逐级处理,在请求到达下游服务之前和收到响应之后都可以进行流水线式处理加工信息。

Predicate:

  • Path
  • Host
  • Date/Time
  • Method
  • Headers
  • Query Params
  • Cookies
  • Read Body

Pre-Filter:

  • Headers
  • Path
  • Rate Limiting
  • Cookies
  • Hystrix
  • Modifying Body

Global-Filter:

  • Netty Router
  • Web Sockets
  • Load Balancers
  • Metrics

Post-Filter:

  • Set status
  • Headers
  • Cookies


First try

  为使用Gateway只需要引入以下的依赖:

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

当然,为了加入spring-boot的监控,可引入以下jar包:

在这里插入代码片

  代码示例:

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
	return builder.routes().route(r -> r.path("/csdn").uri("https://blog.csdn.net/qq_39722475").id("my_blog")).build();
}

在这里插入图片描述



Predicate

通过gateway的启动日志可以看到Predicate的种类:
在这里插入图片描述

 1. After

  表示在某一时间之后,才允许访问。我们启动一个8081的服务,用于接收gateway发来的请求。单独访问打印Hello!
在这里插入图片描述
  Gateway的after断言处理,

@Bean
	public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
		ZonedDateTime datetime = LocalDateTime.now().minusHours(1).atZone(ZoneId.systemDefault());
		return builder.routes().route("after_route", r -> r.after(datetime).uri("http://127.0.0.1:8081/")).build();
	}

  使用配置文件

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: http://127.0.0.1:8081/
        predicates:
        - After=2020-05-31T21:48:45.091+08:00[Asia/Shanghai]

在这里插入图片描述
  如果更改代码,允许一个小时后访问:

	@Bean
	public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
		ZonedDateTime datetime = LocalDateTime.now().plusHours(1).atZone(ZoneId.systemDefault());
		return builder.routes().route("after_route", r -> r.after(datetime).uri("http://127.0.0.1:8081/")).build();
	}

在这里插入图片描述

 2. Before

  表示在某一时间之前,才允许访问。java代码:

@Bean
	public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
		ZonedDateTime datetime = LocalDateTime.now().plusHours(1).atZone(ZoneId.systemDefault());
		return builder.routes().route("before_route", r -> r.before(datetime).uri("http://127.0.0.1:8081/")).build();
	}

  配置文件方式:

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: http://127.0.0.1:8081/
        predicates:
        - Before=2020-05-31T21:48:45.091+08:00[Asia/Shanghai]

 3. Between

  表示在某一时间之前,才允许访问。java代码:

	@Bean
	public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
		ZonedDateTime datetime1 = LocalDateTime.now().minusHours(1).atZone(ZoneId.systemDefault());
		ZonedDateTime datetime2 = LocalDateTime.now().plusHours(1).atZone(ZoneId.systemDefault());
		return builder.routes().route("between_route", r -> r.between(datetime1, datetime2).uri("http://127.0.0.1:8081/")).build();
	}

  配置文件方式:

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: http://127.0.0.1:8081/
        predicates:
        - Between=2020-05-31T20:48:45.091+08:00[Asia/Shanghai], 2020-05-31T22:48:45.091+08:00[Asia/Shanghai]

 4. Cookie

	@Bean
	public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
		return builder.routes().route("cookie_predicate_route", r -> r.cookie("name", "xiaoming").uri("http://127.0.0.1:8081/")).build();
	}
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: http://127.0.0.1:8081/
        predicates:
        - Cookie=name, xiaoming

  访问的截图:
在这里插入图片描述

 5. Header

  表示请求的Header有相应的字段,满足给定的正则表达式,才允许访问。java代码:

@Bean
	public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
		return builder.routes().route("header_route", r -> r.header("X-Request-Id", "\\d+").uri("http://127.0.0.1:8081/")).build();
	}

  配置文件方式:

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: http://127.0.0.1:8081/
        predicates:
        - Header=X-Request-Id, \d+

  访问的截图:
在这里插入图片描述

 6. Host

  参数是一系列的域名。可以使用Ant式的匹配风格,如**.sunq.com匹配www.sunq.com、vip.sunq.com等。也可以是{sub}.sunq.com,这个sub取自ServerWebExchange.getAttributes()的key是ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE时的value。java代码:

	@Bean
	public RouteLocator customRouteLocator4(RouteLocatorBuilder builder) {
		ZonedDateTime datetime = LocalDateTime.now().plusHours(1).atZone(ZoneId.systemDefault());
		return builder.routes().route("host_predicate_route", r -> r.host("sunq.com:8080").uri("http://127.0.0.1:8081/")).build();
	}

  配置文件方式:

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://127.0.0.1:8081/
        predicates:
        - Host=**.sunq.com, **.buns.com

  更改本地的host:
在这里插入图片描述
  结果示例:
在这里插入图片描述
  此时再用localhost访问不到了:
在这里插入图片描述

 7. Method

  请求的方法与之匹配,可支持配置多个,以逗号隔开。java代码:

	@Bean
	public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
		return builder.routes().route("method_route", r -> r.method(HttpMethod.GET).uri("http://127.0.0.1:8081/")).build();
	}

  配置文件方式:

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: http://127.0.0.1:8081/
        predicates:
        - Method=GET

 8. Path

  请求的路径与之匹配,可支持配置多个,以逗号隔开。java代码:

	@Bean
	public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
		return builder.routes().route("method_route", r -> r.method(HttpMethod.GET).uri("http://127.0.0.1:8081/")).build();
	}

  配置文件方式:

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: http://127.0.0.1:8081/
        predicates:
        - Path=/test/{segment}

Filter

 1. AddRequestHeader

  给请求加上头信息:

	@Bean
	public RouteLocator customRouteLocator3(RouteLocatorBuilder builder) {
		return builder.routes().route("head_filter", r -> r.path("/test/head").filters(f -> f.addRequestHeader("X-Request-Id", "hello!")).uri("http://localhost:8081/test/head")).build();
	}

  配置文件方式:

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: http://127.0.0.1:8081/
        filters:
        - AddRequestHeader=X-Request-red, hello!

  8081的代码:

	@GetMapping("/test/head")
	public String testGatewayHead(HttpServletRequest request, HttpServletResponse response){
		String head=request.getHeader("X-Request-Id");
		return "return head info:"+head;
	}

在这里插入图片描述

  由于到达Filter已经知晓了uri,因此可以在配置上使用路径或域名的字段:

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment}
        filters:
        - AddRequestHeader=X-Request-Red, Blue-{segment}

  以上的配置可以访问https://example.org/red/1,就会在头加上X-Request-Red=Blue-1。

 2. AddRequestParameter

  添加请求参数。

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: https://example.org
        filters:
        - AddRequestParameter=red, blue

 3. AddResponseHeader

  添加回执头信息。

spring:
  cloud:
    gateway:
      routes:
      - id: add_response_header_route
        uri: https://example.org
        filters:
        - AddResponseHeader=X-Response-Red, Blue

 4. DedupeResponseHeader

  对回执头信息去重。比如,我们在Gateway以及微服务上都设置了CORS(解决跨域)header,如果不做任何配置,请求 -> 网关 -> 微服务,获得的响应就是这样的:

Access-Control-Allow-Credentials: true, true
Access-Control-Allow-Origin: https://musk.mars, https://musk.mars

也就是Header重复了。要想把这两个Header去重,只需设置成如下即可1

spring:
  cloud:
    gateway:
      routes:
      - id: dedupe_response_header_route
        uri: https://example.org
        filters:
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_FIRST

 5. HystrixGatewayFilter

  Hystrix在网关引入断路器,防止服务调用的级联失败。需要引入spring-cloud-starter-netflix-hystrix的依赖jar。配置示例:

spring:
  cloud:
    gateway:
      routes:
      - id: hystrix_route
        uri: https://example.org
        filters:
        - Hystrix=myCommandName

  因为官方的推荐是Resilience4J,对此不过多说明(自身也不熟)。

 6. Spring Cloud CircuitBreakerFilter

  Spring Cloud CircuitBreaker是对Hystirx和Resilience4J的包装。配置的方式:

spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: https://example.org
        filters:
        - name: CircuitBreaker
          args:
            name: myCircuitBreaker
            fallbackUri: forward:/inCaseOfFailureUseThis

 7. FallbackHeaders

  当发生熔断时,此过滤器允许我们把异常信息放到转发的请求头信息中。配置的方式:

spring:
  cloud:
    gateway:
      routes:
      - id: ingredients
        uri: lb://ingredients
        predicates:
        - Path=//ingredients/**
        filters:
        - name: CircuitBreaker
          args:
            name: fetchIngredients
            fallbackUri: forward:/fallback
      - id: ingredients-fallback
        uri: http://localhost:9994
        predicates:
        - Path=/fallback
        filters:
        - name: FallbackHeaders
          args:
            executionExceptionTypeHeaderName: Test-Header

 8. MapRequestHeader

  把请求的头信息赋值给回执的头信息:

spring:
  cloud:
    gateway:
      routes:
      - id: map_request_header_route
        uri: https://example.org
        filters:
        - MapRequestHeader=Blue, X-Request-Red

这会把Blue的value赋值给X-Request-Red。

 9. PrefixPath

  对所有的请求路径添加前缀:

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
        - PrefixPath=/mypath

访问/hello的请求被发送到/mypath/hello。

 10. PreserveHostHeader

  表示在Spring Cloud Gateway转发请求的时候,保持客户端的Host信息不变,会将这些信息携带到后面的服务实例上面。要不然后面的实例服务接收到的Host信息就是新的Http Client的Host信息,而不是真实的客户端的Host信息了。配置如下所示,这个配置没有参数:

spring:
  cloud:
    gateway:
      routes:
      - id: preserve_host_route
        uri: https://example.org
        filters:
        - PreserveHostHeader

 11. RequestRateLimiter

  限流使用RateLimiter的实现类来决定当前的请求是否处理,不处理就返回429(太多请求)。目前的一个实现是ReidsRateLimiter,需要引入spring-boot-starter-data-redis-reactive的依赖。此限流的策略是令牌桶,有3个对应的参数:

  • redis-rate-limiter.replenishRate:令牌桶允许的填充速率。
  • redis-rate-limiter.burstCapacity:令牌桶的最大容量。
  • redis-rate-limiter.requestedTokens:每个请求消耗的令牌数目。

另外有个可选参数key-resolver,对应的接口是KeyResolver,现在默认的实现是PrincipalNameKeyResolver。用于从请求抽取key,如果找不到会直接拒绝请求。可以调整以下配置spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key (true or false) 和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code。
  配置如下所示:

spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: https://example.org
        filters:
        - name: RequestRateLimiter
          args:
            redis-rate-limiter.replenishRate: 1000
            redis-rate-limiter.burstCapacity: 2000
            redis-rate-limiter.requestedTokens: 1

 12. RedirectTo

  重定向,配置包含重定向的返回码和地址:

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
        - RedirectTo=302, https://acme.org

 13. RemoveRequestHeader

  去掉某个请求头信息:

spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: https://example.org
        filters:
        - RemoveRequestHeader=X-Request-Foo

 14. RemoveResponseHeader

  去掉某个回执头信息:

spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: https://example.org
        filters:
        - RemoveResponseHeader=X-Request-Foo

 15. RemoveRequestParameter

  去掉某个请求参数信息:

spring:
  cloud:
    gateway:
      routes:
      - id: removerequestparameter_route
        uri: https://example.org
        filters:
        - RemoveRequestParameter=red

 16. RewritePath

  改写路径:

	@Bean
	public RouteLocator customRouteLocator6(RouteLocatorBuilder builder) {
		return builder.routes().route("rewrite_filter", r -> r.path("/where/**").filters(f -> f.rewritePath("/where/*", "/test/")).uri("http://localhost:8081/test/head")).build();
	}

  或使用配置:

spring:
  cloud:
    gateway:
      routes:
      - id: rewrite_filter
        uri: http://localhost:8081
        predicates:
        - Path=/test/**
        filters:
        - RewritePath=/where(?<segment>/?.*), /test(?<segment>/?.*)

  路径中的where改为了test。在这里插入图片描述

 17. RewriteLocationResponseHeader

  改写回执的版本信息,改写回执的host:port等。

spring:
  cloud:
    gateway:
      routes:
      - id: rewritelocationresponseheader_route
        uri: http://example.org
        filters:
        - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,

 18. RewriteResponseHeader

  改写回执的头信息。

spring:
  cloud:
    gateway:
      routes:
      - id: rewriteresponseheader_route
        uri: https://example.org
        filters:
        - RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=***

/42?user=ford&password=omg!what&flag=true被改写为/42?user=ford&password=***&flag=true。

 19. RewriteResponseHeader

  强制WebSession::save操作。

spring:
  cloud:
    gateway:
      routes:
      - id: save_session
        uri: https://example.org
        predicates:
        - Path=/foo/**
        filters:
        - SaveSession

 20. SecureHeaders

  添加安全相关头信息:

  • X-Xss-Protection:1 (mode=block)
  • Strict-Transport-Security (max-age=631138519)
  • X-Frame-Options (DENY)
  • X-Content-Type-Options (nosniff)
  • Referrer-Policy (no-referrer)
  • Content-Security-Policy (default-src ‘self’ https:; font-src ‘self’ https: data:; img-src ‘self’ https: data:; object-src ‘none’; script-src https:; style-src ‘self’ https: ‘unsafe-inline)’
  • X-Download-Options (noopen)
  • X-Permitted-Cross-Domain-Policies (none)

 21. SetPath

  设置请求路径,与RewritePath类似。

spring:
  cloud:
    gateway:
      routes:
      - id: setpath_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment}
        filters:
        - SetPath=/{segment}

如/red/blue的请求被转发到/blue。

 22. SetRequestHeader

  设置请求头信息。

spring:
  cloud:
    gateway:
      routes:
      - id: setrequestheader_route
        uri: https://example.org
        filters:
        - SetRequestHeader=X-Request-Red, Blue

 23. SetResponseHeader

  设置回执头信息。

spring:
  cloud:
    gateway:
      routes:
      - id: setrequestheader_route
        uri: https://example.org
        filters:
        - SetResponseHeader=X-Request-Red, Blue

 24. SetStatus

  设置回执状态码。

spring:
  cloud:
    gateway:
      routes:
      - id: setstatusint_route
        uri: https://example.org
        filters:
        - SetStatus=401

 25. StripPrefix

  跳过指定路径。

spring:
  cloud:
    gateway:
      routes:
      - id: nameRoot
        uri: https://nameservice
        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2

请求/name/blue/red会转发到/red。

 26. Retry

  重试。

spring:
  cloud:
    gateway:
      routes:
      - id: retry_test
        uri: http://localhost:8080/flakey
        predicates:
        - Host=*.retry.com
        filters:
        - name: Retry
          args:
            retries: 3
            statuses: BAD_GATEWAY
            methods: GET,POST
            backoff:
              firstBackoff: 10ms
              maxBackoff: 50ms
              factor: 2
              basedOnPreviousValue: false

 27. RequestSize

  请求大小。

spring:
  cloud:
    gateway:
      routes:
      - id: request_size_route
        uri: http://localhost:8080/upload
        predicates:
        - Path=/upload
        filters:
        - name: RequestSize
          args:
            maxSize: 5000000

超过5M的请求会返回413错误。

 28. SetRequestHost

  设置请求的host。

spring:
  cloud:
    gateway:
      routes:
      - id: set_request_host_header_route
        uri: http://localhost:8080/headers
        predicates:
        - Path=/headers
        filters:
        - name: SetRequestHost
          args:
            host: example.org

 29. ModifyRequestBody

  更改请求体。

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
                    (exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))
        .build();
}

static class Hello {
    String message;

    public Hello() { }

    public Hello(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

 30. ModifyResponseBody

  更改回执体。

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyResponseBody(String.class, String.class,
                    (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri)
        .build();
}

 31. Default-filters

  对所有请求添加过滤器。

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Red, Default-Blue
      - PrefixPath=/httpbin

动态路由

  通过不停机,更新路由规则。

public class GatewayRouteDefinition {
	// 路由的id
	private String id; 
	// 路由断言集合配置
	private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
	// 路由过滤器集合配置
	private List<GatewayFilterDefinition> filters = new ArrayList<>();
	// 珞由规则转发的目标uri
	private String uri; 
	// 路由执行的顺序
	private int order= O; 
	// 此处省略get和set方法
}
public class GatewayFilterDefinition {
	//Filter Name 
	private String name; 
	// 对应的路由规则
	private Map<String, String> args =口ew LinkedHashMap<>(); 
	// 此处省略Get和Set方法
}
public class GatewayPredicateDefinition {
	// 断言对应的Name
	private String name; 
	// 配置的断言规则
	private Map<String, String> args = new LinkedHashMap<> (); 
	// 此处省略Get和Set方法
}
@Service 
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {
	@Autowired 
	private RouteDefinitionWriter routeDefinitionWriter; 
	private ApplicationEventPublisher publisher; 
	// 增加路由
	public String add(RouteDefinitiondefinition definition) { 
		routeDefinitionWriter.save(Mono.just(definition)).subscribe(); 
		this.publisher.publishEvent(new RefreshRoutesEvent(this)); 
		return "success"; 
	}
	// 更新路由
	public String update(RouteDefinition definition) {
		try { 
			this.routeDefinitionWriter.delete(Mono.just(definition.getld()));
		} catch (Exception e) {
			return "update fail,not find route routeld:" + definition.getid();
		}
		try {
			routeDefinitionWriter.save(Mono.just(definition)).subscribe();
			this.publisher.publishEvent(new RefreshRoutesEvent(this)); 
			return "success"} catch (Exception e) {
			return "update route fail";
		}
	}
	// 删除路由
	public Mono<ResponseEntity<Object> delete(Stringid) {
		return this.routeDefinitionWriter.delete(Mono.just(id))
			.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
			.onErrorResume(t -> t instanceof NotFoundException, t -> Mono.
			just(ResponseEntity.notFound().build()));
	}
	@Override
	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { 
		this.publisher = applicationEventPublisher;
	}
}
@RestController 
@RequestMapping("/route")
public class RouteController {
	@Autowired 
	private DynamicRouteServiceimpl dynamicRouteService; 
	// 增加路由
	@PostMapping("/add")
	public String add(@RequestBody GatewayRouteDefinition gwdefinition) {
		try {
			RouteDefinition definition = assembleRouteDefinition(gwdefinition);
			return this.dynamicRouteService.add(definition); 
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "succss";
	}
	// 删除路由
	@DeleteMapping("/routes/{id}")
	public Mono<ResponseEntity<Object> delete(@PathVariable String id) {
		return this.dynamicRouteService.delete(id);
	}
	// 更新路由
	@PostMapping("/update")
	public String update(@RequestBody GatewayRouteDefinition gwdefinition) {
		RouteDefinition definition = assembleRouteDefinition(gwdefinition);
		return this.dynamicRouteService.update(definition); 
	}
}

  1. 参考https://www.imooc.com/article/290816 ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值