1 简易网关工程
pom文件:
<?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>
<packaging>jar</packaging>
<parent>
<groupId>com.tdt.platform.cloud</groupId>
<artifactId>platform-cloud-b-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>cloud-gateway</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- mac -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver-dns-native-macos</artifactId>
<version>4.1.79.Final</version>
<classifier>osx-aarch_64</classifier>
</dependency>
</dependencies>
</project>
配置文件:
server:
port: 8000
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: product_route
uri: http://localhost:9002
order: 1
predicates:
- Path=/cloud-product-service/**
filters:
- StripPrefix=1
启动类:
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
然后启动网关工程,请求接口如图:
这样一个简易的网关就搭建好了。
2 整合nacos注册中心
引入依赖:
<!-- nacos discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- loadbalancer: 新版本cloud已经移除了ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
启动类增加注解@EnableDiscoveryClient
配置文件中增加nacos相关配置:
server:
port: 8000
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
# 让gateway可以发现nacos中的微服务
enabled: false
routes:
# 路由标识
- id: product_route
# 路由指向的目的地,也就是说被转发到那个服务上
# uri: http://localhost:9002
uri: lb://cloud-product-service
# 断言条件判断,所有断言都验证通过,才会执行真正的路由
predicates:
# 以/cloud-product-service开头的请求
- Path=/cloud-product-service/**
# 过滤器
filters:
- StripPrefix=1
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: dev
3 gateway原理
gateway中请求的执行流程,如图:
执行流程大体如下:
- Gateway Client向Gateway Server发送请求
- 请求首先会被HttpWebHandlerAdapter进行提取组装成网关上下文
- 然后网关的上下文会传递到DispatcherHandler,它负责将请求分发给 RoutePredicateHandlerMapping
- RoutePredicateHandlerMapping负责路由查找,并根据路由断言判断路由是否可用
- 如果断言成功,由FilteringWebHandler创建过滤器链并调用
- 请求会依次次经过PreFilter–微服务–PostFilter的方法,最终返回响应
4 断言
Predicate(断言, 谓词) 用于进行条件判断,只有断言都返回真,才会真正的执行路由。 断言就是说: 满足什么条件才能进行路由转发。
该配置有坑: spring.cloud.gateway.discovery.locator.enabled
4.1 内置路由断言工厂
4.1.1 基于Datetime类型的断言工厂
AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
– After=2013-01-20T17:42:47.789-07:00[America/Denver]
BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
– Before=2013-01-20T17:42:47.789-07:00[America/Denver]
BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内
– Between=Before=2013-01-20T17:42:47.789-07:00[America/Denver],Before=2013-01-20T17:42:47.789-07:00[America/Denver]
4.1.2 基于远程地址的断言工厂
RemoteAddrRoutePredicateFactory接收一个IP地址段,判断请求主 机地址是否在地址段中
– RemoteAddr=192.168.1.1/24
4.1.3 基于Cookie的断言工厂
CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求中cookie是否具有给定名称且值与正则表达式匹配
– Cookie=chocolate, ch.
4.1.4 基于Header的断言工厂
HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否具有给定名称且值与正则表达式匹配
– Header=X-Request-Id, \d+
4.1.5 基于Host的断言工厂
HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
– Host=**.spring.org
4.1.6 基于Method请求方法的断言工厂
MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
– Method=GET
4.1.7 基于Path请求路径的断言工厂
PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
– Path=/order-service/**
4.1.8 基于Query请求参数的断言工厂
QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。
– Query=baz, ba.
4.1.9 基于路由权重的断言工厂
WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
routes:
- id: weight_route1
uri: host1
predicates:
- Path=/product/**
- Weight=group3, 1
- id: weight_route2
uri: host2
predicates:
- Path=/product/**
- Weight= group3, 9
4.2 自定义路由断言工厂
需求: 仅仅让年龄age在【minAge, maxAge】范围之间的请求通过。
首先在配置中添加Age断言:
– Age 18,65
自定义断言工厂:
@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.AgeRoutePredicateConfig> {
public AgeRoutePredicateFactory() {
super(AgeRoutePredicateConfig.class);
}
// 用于从配置文件中获取参数值赋值到配置类中的属性上
@Override
public List<String> shortcutFieldOrder() {
// 这里的顺序要跟配置文件中的参数顺序一致
return Arrays.asList("minAge", "maxAge");
}
@Override
public Predicate<ServerWebExchange> apply(AgeRoutePredicateConfig config) {
return new Predicate<ServerWebExchange>() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
String age = serverWebExchange.getRequest().getQueryParams().getFirst("age");
if (StringUtils.hasText(age)) {
int targetAge = Integer.parseInt(age);
return targetAge >= config.getMinAge() && targetAge <= config.getMaxAge();
}
return false;
}
};
}
/**
* 用于接收配置文件中的参数
*/
public static class AgeRoutePredicateConfig {
private int minAge;
private int maxAge;
public int getMinAge() {
return minAge;
}
public void setMinAge(int minAge) {
this.minAge = minAge;
}
public int getMaxAge() {
return maxAge;
}
public void setMaxAge(int maxAge) {
this.maxAge = maxAge;
}
}
}
测试: