
1.创建网关模块
1
2
3
4
5
6
7
8
9
10
|
<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>
</dependencies>
|

2.配置规则
需**求1:客户端发送/api/order/转到service-order
**需求2:客户端发送/api/product/转到service-product
需求3:以上转发均有负载均衡效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
spring:
cloud:
gateway:
routes: # - id: service_a
- id: order-route
uri: lb://service-order
predicates:当请求路径以 /api/product/ 开头时,请求会被路由到 service-product 服务: # - Path=/service-a/**
- Path=/api/order/**
- id: product-route
uri: lb://serivce-product
predicates: # - Path=/service-a/**
- Path=/api/product/**
//id:唯一标识这个路由规则
//uri:lb://service-order //表示使用 Spring Cloud LoadBalancer 进行客户端负载均衡
//service-order是服务注册中心中的服务名
//predicates:当请求路径以 /api/product/ 开头时,请求会被路由到 service-product 服务
这些路由规则有自己的执行循序:默认从上往下执行,如果想要执行更快可以使用order: 1规定执行顺序
|

1
2
3
4
5
6
7
8
9
10
11
12
|
- id: bingo-route # 路由的唯一标识符
uri: https://www.bing.com # 目标URL(直接转发到Bing)
predicates: # 路由匹配条件
- name: Path # 第一个条件:路径匹配
args:
pattern: /search # 只匹配以/search开头的路径
- name: Query # 第二个条件:查询参数匹配
args:
param: q # 检查名为"q"的查询参数
regex: haha # 该参数值必须匹配正则表达式"haha"
这个规则表示只有请求路径为:search?q=haha请求才会被转发到bing.com中
|
2.1.自定义断言工厂
1.一个类的名字要写成xxxRoutePredicateFactory.Config,并且要继承AbstractRoutePredicateFactory,泛型为类名
2.必须重写父类的shortcutFieldOrder和apply方法,apply方法代码逻辑主要写业务代码逻辑
如以下代码是判断用户是否为vip用户的业务逻辑
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
package com.example.gateway.predicate;
import jakarta.validation.constraints.NotEmpty;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
@Component
public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory<VipRoutePredicateFactory.Config> {
public VipRoutePredicateFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("param","value");
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
//localhost/search?q=haha&user=Lsec
ServerHttpRequest request = exchange.getRequest();
String first = request.getQueryParams().getFirst(config.getParam());
if (StringUtils.hasText(first) && first.equals(config.getValue())) {
return true;
}
return false;
}
};
}
/*
* 可以配置的参数
* */
public static class Config{
@NotEmpty
private String param;
@NotEmpty
private String value;
public String getParam() {
return param;
}
public void setParam(String param) {
this.param = param;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
//配置文件规则
- name: Vip
args:
param: user
value: Lsec
|
3.Filter过滤器
3.1.过滤器Filter基本使用

**路径重写Filter:**RewritePath,什么是路径重写,当你想使用/api/product这个基准路径访问业务时,需要在product模块的controller层代码添加对应的基准路径,有时你会觉得不方便,如果使用路径重写filter,你就可以不用在controller层添加基准路径的api,直接访问即可
1
2
3
4
5
6
7
8
9
|
- id: product-route
uri: lb://serivce-product
predicates: # - Path=/service-a/**
- Path=/api/product/**
filters:
- RewritePath=/api/product/?(?<segment>.*),/$\{segment}
//修改请求的路径后再转发到目标服务
//请求 /api/product/123 → 重写为 /123 → 转发到 service-product/123
//请求 /api/product/items → 重写为 /items → 转发到 service-product/items
|
**添加请求头响应头Filter:**AddRequestHeader/AddResponseHeader
1
2
3
4
5
6
7
8
9
10
11
12
|
spring:
cloud:
gateway:
routes: # - id: service_a
- id: order-route
uri: lb://service-order
predicates: # - Path=/service-a/**
- Path=/api/order/**
filters:
- RewritePath=/api/order/?(?<segment>.*),/$\{segment}
- AddRequestHeader=X-Response-ABC, 123
- AddResponseHeader=X-Response-Foo, Bar
|
就是在请求包中添加请求头和响应头
3.2.默认Filter
如果你的路由没有写Filter,但是存在默认Filter,路由默认会使用这个Filter
1
2
3
4
5
|
default-filters:
- name: AddRequestHeader
args:
name: X-Request-Foo
value: Bar
|
3.3.全局Filter
想要实现全局filter,需要在实现GlobalFilter,并且重写filter方法
下面是一个计算请求时间的全局过滤器
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
27
28
29
30
31
32
33
34
|
package com.example.gateway.filter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class RtGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String uri = request.getURI().toString();
long start = System.currentTimeMillis();
log.info("请求开始,uri:{},时间:{}ms", uri, start);
/*==============================================*/
Mono<Void> filter = chain.filter(exchange)
.doFinally(result -> {
long end = System.currentTimeMillis();
log.info("请求结束,uri:{},时间:{}ms", uri, (end - start));
});
return filter;
}
}
|
3.4.自定义过滤器工厂
自定义过滤器工厂和自定义断言工厂一样,可以看Spring Cloud自带的过滤器工厂是怎么设置的,根据样式仿写即可

可以看到系统的工厂需要继承AbstractNameValueGatewayFilterFactory这个父类工厂,并重写apply方法
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
package com.example.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.util.UUID;
@Component
public class OnceTokenGatewayFilterFactory extends AbstractGatewayFilterFactory<OnceTokenGatewayFilterFactory.Config> {
public OnceTokenGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> chain.filter(exchange).then(Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
HttpHeaders headers = response.getHeaders();
String value = config.getValue();
if (value == null || value.isEmpty()) {
value = UUID.randomUUID().toString(); // 默认生成 UUID
} else if (value.equals("uuid")) {
value = UUID.randomUUID().toString();
} else if (value.equals("jwt")) {
value = "jwt_token";
}
headers.add("once-token", value);
}));
}
public static class Config {
private String value; // 移除 @NotEmpty
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
|
4.全局跨域(CORS)
4.1.什么是跨域
1
2
3
4
5
6
7
8
|
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]': # 匹配所有请求
allowedOrigins: "*" #跨域处理 允许所有的域
allowedMethods: "*"# 支持的方法
|

经典面试题:微服务之间的调用是否经过网关
默认是不经过网关的,但是也可以经过网关,只需要将远程调用的地址改为网关地址,让网关来发送请求即可
