Gateway
Gateway 简介
- 是一个构建在Spring生态系统之上的API网关,基于spring5、springboot2和projectreactor。springcloudgateway旨在提供一种简单而有效的方法来路由到api,并为api提供横切关注点,比如:安全性、监控/度量和弹性。
具有如下特性:
- 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建;
- 能够在任何请求属性上匹配路由;
- 可以对路由指定 Predicate(断言)和 Filter(过滤器);
- 集成了断路器;
- 集成了SpringCloud服务发现功能;
- 易于自定义 Predicate(断言)和 Filter(过滤器);
- 限流;
- 路径重写。
以上来源于官方文档https://spring.io/projects/spring-cloud-gateway
术语
- Route(路由):网关的基本模块。它由一个ID、一个目标URI、一组断言和一组过滤器定义。如果断言为true,则匹配该路由。
- Predicate(断言):这是一个Java8的Function Predicate,输入类型是spring框架内的ServerWebExchange,这使得开发人员可以匹配HTTP请求中的任何内容,例如头或参数。
- Filter(过滤器):这些是用特定工厂构建的spring框架内的 GatewayFilter 实例。可以在发送下游请求之前或之后修改请求和响应。
创建GateWay模块
- 基于nacos作为服务中心和配置中心添加gateway
https://blog.csdn.net/qq_40977118/article/details/109181938
1.pom.xml
- 这里需要注意的是不能引入spring-boot-starter-web这个jar包,不能是一个springweb项目
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gateway</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<!--springcloud版本号-->
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
<!--springcloudalibaba版本号-->
<spring.cloud.alibaba.version>2.2.3.RELEASE</spring.cloud.alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--服务注册-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. bootstrap.yml
- 这里之配了nacos的配置信息,端口和路由规则都后面会配置在nacos中,就不需要重启项目了
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
3. 启动类
package com.example.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
4. 启动服务
- nacos中gateway的配置文件
- 配置了端口号,还有路由规则
Route Predicate Factories
- 下面介绍一些常用的Route Predicate,参考官方文档https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gateway-request-predicates-factories
1. After Route
- 在指定时间之后的请求会匹配该路由,+08:00表示东八区
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://www.baidu.com/
predicates:
- After=2017-01-20T17:42:47+08:00[Asia/Shanghai]
- http://localhost:9090/
2. Before Route
- 在指定时间之前的请求会匹配该路由
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://www.taobao.com/
predicates:
- Before=2022-12-29T18:30:00+08:00[Asia/Shanghai]
- http://localhost:9090/
3. Between Route
- 在指定时间区间内的请求会匹配该路由
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://www.iqiyi.com/
predicates:
- Between=2017-01-20T17:42:47+08:00[Asia/Shanghai],2022-12-29T18:30:00+08:00[Asia/Shanghai]
4. Cookie Route
- 带有指定Cookie的请求会匹配该路由
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://www.qq.com/
predicates:
- Cookie=name,fisher
- 使用curl发送带有cookie为name=fisher的请求
curl http://localhost:9090/ --cookie "name=fisher"
5. Header Route
- 带有指定请求头的请求会匹配该路由
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://www.jd.com/
predicates:
- Header=X-Request-Id, \d+
- 发送请求头为X-Request-Id:123的请求
curl http://localhost:9090/ -H "X-Request-Id:123"
6. Host Route
- 带有指定Host的请求会匹配该路由
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://www.baidu.com/
predicates:
- Host=activate.navicat.com:9090,**.fisher.com
- host文件配置
- http://activate.navicat.com:9090/
- 使用curl发送请求头带有Host的请求
curl http://localhost:9090/ -H "Host:www.fisher.com"
7. Method Route
- 发送指定方法的请求会匹配该路由
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://www.baidu.com/
predicates:
- Method=GET
- get请求http://localhost:9090/
- 使用post方式请求
curl -X POST http://localhost:9090/
8. Path Route
- 发送指定路径的请求会匹配该路由
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://localhost:8081
predicates:
- Path=/order/{
segment}
- id: host_route2
uri: http://localhost:8091
predicates:
- Path=/web/{
segment}
- id: host_route3
uri: https://www.baidu.com
predicates:
- Path=/s
- http://localhost:8081/order/index
- http://localhost:8091/web/index
- 请求http://localhost:9090/order/index,路由到order工程
- 请求http://localhost:9090/web/index,路由到web工程
- 请求http://localhost:9090/s,路由到百度
- 请求http://localhost:9090/,会404
9. Query Route
- 带指定查询参数的请求可以匹配该路由
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://www.baidu.com/
predicates:
- Query=green
- http://localhost:9090/?green
10. RemoteAddr Route
- 从指定远程地址发起的请求可以匹配该路由
- 127.0.0.1是客户端的ip地址,24是子网掩码
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://www.baidu.com/
predicates:
- RemoteAddr=127.0.0.1/24
- 请求http://127.0.0.1:9090/
11. Weight Route
- 使用权重来路由相应请求,80%的请求会到百度,20%的请求会到腾讯
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://www.baidu.com/
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://www.qq.com/
predicates:
- Weight=group1, 2
Custom Global Filters
- 自定义全局filter,可以用@Bean的方式,也可以实现GlobalFilter接口
- 检验cookie
package com.example.gateway;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Objects;
@Component
public class CustomGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, HttpCookie> cookies = request.getCookies();
//验证是否存在key=fisher的cookie
for (String s : cookies.keySet()) {
if (Objects.equals(s, "fisher")) {
System.out.println("cookies验证成功");
return chain.filter(exchange);
} else {
System.out.println("cookies不存在");
}
}
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
}
- 请求curl http://localhost:9090/ --cookie “fisher=aaa”
- 直接请求http://localhost:9090/,返回错误码401
Custom GatewayFilter Factories
- 自定义局部filter
- 配置路由规则,路由带有green参数的请求,
lb://micro-order
表示负载均衡到micro-order服务上,Custom表示自定义路由的名称(过滤器的名称可以随便取,但必须是这个名称后面+GatewayFilterFactory,不然gateway识别不了Custom这个名字),true,2是要传到过滤器中的参数(如果过滤器不需要也可以不传)
spring:
cloud:
gateway:
routes:
- id: query_route
uri: lb://micro-order
predicates:
- Query=green
filters:
- Custom=true,2
- 先停用全局filter,只使用这个局部filter,检验cookie
package com.example.gateway;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import java.util.Arrays;
import java.util.List;
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.Config> {
public CustomGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
if (!config.enable) {
return chain.filter(exchange);
}
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, HttpCookie> cookies = request.getCookies();
//验证是否存在key=fisher的cookie
for (String s : cookies.keySet()) {
if (s.equals("fisher")) {
System.out.println("--------CustomGatewayFilterFactory--------------");
return chain.filter(exchange);
}
}
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
};
}
@Override
public List<String> shortcutFieldOrder() {
//表示从路由规则中获取的参数对应哪些key,通过Config中set方法赋值
return Arrays.asList("enable", "count");
}
public static class Config {
private boolean enable;
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
System.out.println("count=" + count);
this.count = count;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
System.out.println("enable=" + enable);
this.enable = enable;
}
}
}
- 启动项目,成功获取路由中配置的参数
- 不带cookie请求http://localhost:9090/order/index?green,返回401
- 带cookie请求
curl http://localhost:9090/order/index?green --cookie "fisher=xxx"
Custom Route Predicate Factories
- 自定义断言规则,需要继承AbstractRoutePredicateFactory,命名规则要求类的名字为断言名+RoutePredicateFactory ,这里使用Custom
spring:
cloud:
gateway:
routes:
- id: query_route
uri: lb://micro-order
predicates:
- Custom=green
- 这里就判断一下查询参数key是否等于green
package com.example.gateway;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
@Component
public class CustomRoutePredicateFactory extends AbstractRoutePredicateFactory<CustomRoutePredicateFactory.Config> {
public CustomRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> queryParams = request.getQueryParams();
for (String s : queryParams.keySet()) {
if (Objects.equals(config.getName(), s)) {
System.out.println("Green is a good color!");
return true;
}
}
System.out.println("other color is bad!");
return false;
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("name");
}
public static class Config {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
- http://localhost:9090/order/index?green
- http://localhost:9090/order/index?red