GateWay简介
Spring Cloud Gateway 为 SpringBoot 应用提供了API网关支持,具有强大的智能路由与过滤器功能,本文将对其用法进行详细介绍。
网关术语
关于网关三个重要的概念如下:
- Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由;
- Predicate(断言):指的是Java 8 的 Function Predicate。 输入类型是Spring框架中的ServerWebExchange。 这使开发人员可以匹配HTTP请求中的所有内容,例如请求头或请求参数。如果请求与断言相匹配,则进行路由;
- Filter(过滤器):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前后对请求进行修改,比如参数效验、权限效验、流量监控、日志输出、协议转换等。
主要功能
Gateway是在Spring生态系统之上构建的API网关服务,Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能, 例如:熔断、限流、重试等。
Spring Cloud Gateway 具有如下特性:
- 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建;
- 动态路由:能够匹配任何请求属性;
- 可以对路由指定 Predicate(断言)和 Filter(过滤器);
- 集成Hystrix的断路器功能;
- 集成 Spring Cloud 服务发现功能;
- 易于编写的 Predicate(断言)和 Filter(过滤器);
- 请求限流功能;
- 支持路径重写。
调用执行过程
- Gateway Client向Gateway Server发送请求
- 请求首先会被HttpWebHandlerAdapter进行提取组装成网关上下文
- 然后网关的上下文会传递到DispatcherHandler,它负责将请求分发给RoutePredicateHandlerMapping
- RoutePredicateHandlerMapping负责路由查找,并根据路由断言判断路由是否可用
- 如果过断言成功,由FilteringWebHandler创建过滤器链并调用
- 请求会一次经过PreFilter--微服务--PostFilter的方法,最终返回响应
为什么要使用网关
看下面的图即可:
- 传统应用部署模式:
- 采用网关后的部署模式:
此测试中使用了Consul注册中心
GateWay网关服务实现
pom.xml
依赖版本可参考 springbootSeries 模块中pom.xml文件中的版本定义
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
application.yml 配置
因为网关配置比较复杂,会有一些数组类的配置,所以这里选用yml格式。
yaml
server:
port: 19000
debug: false
spring:
application:
name: springbootAPIGateway
profiles:
active: dev
cloud:
consul:
discovery:
service-name: ${spring.application.name}
host: localhost
port: 8500
gateway:
routes:
- id: path_route #路由标识、区别于其他route
order: 9000 #用于多个route之间的排序,数值越小排序越靠前,匹配优先级越高
uri: http://localhost:18096 # 路由指向的目的地uri,即客户端请求最终被转发到的微服务
predicates:
- Path=/api/load/v1/** #断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由
main:
web-application-type: reactive
SpringbootApplication启动类
这里主要是添加了Consul服务发现注解@EnableDiscoveryClient
java
@Slf4j
@SpringBootApplication(scanBasePackages = {"com.korgs", "cn.hutool.extra.spring"})
@Configuration
@EnableConfigurationProperties
@ServletComponentScan
@RestController
@EnableDiscoveryClient
public class SpringbootApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApiGatewayApplication.class, args);
}
@GetMapping("/helloworld")
public BaseResponse helloWorld(){
log.info( LogGenerator.trackLog()
+ "msg=" + "I am busy to handle this request.");
return BaseResponse.success("hello world");
}
}
被代理的springboot服务
这个就是一个普通的应用,没什么特殊的。
pom.xml
这里主要配置了注册中心,如果不需要可删除掉consul相关的配置
xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
</dependencies>
application.properties 配置
这里主要配置了注册中心,如果不需要可删除掉consul相关的配置
shell
spring.profiles.active = dev
spring.application.name=springbootGatewayServer
server.port=18096
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
spring.cloud.consul.discovery.service-name=${spring.application.name}
SpringbootApplication启动类
java
@Slf4j
@SpringBootApplication(scanBasePackages = {"com.korgs", "cn.hutool.extra.spring"})
@Configuration
@EnableConfigurationProperties
@ServletComponentScan
@RestController
@EnableDiscoveryClient
public class SpringbootGatewayServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootGatewayServerApplication.class, args);
}
@GetMapping("/helloworld")
public String helloWorld(){
log.info( LogGenerator.trackLog()
+ "msg=" + "I am busy to handle this request.");
return "hello world";
}
}
实现被代理的Restful API
java
@Slf4j
@RestController
@RequestMapping("/api/load")
public class LoadBalanceController {
@Value("${server.port}")
private String serverPort;
@GetMapping("/v1/hello-content/{uuid}")
public BaseResponse<String> loadHelloContent(@PathVariable String uuid){
String str = LogGenerator.trackLog()
+ " uuid=" + uuid + " I am busy to handle this request."
+ " serverPort=" + serverPort;
log.info( str );
return BaseResponse.success(str);
}
}
源码下载
涉及模块:
- springcloud-api-gateway:19000 ,网关服务
- springcloud-gateway-server:18096 ,这个就是一个普通的应用,没什么特殊的
源码下载:
源码运行方法:
访问 : http://localhost:19000/api/load/v1/hello-content/2
会发现端口号19000不变,但实际访问了18096服务的实现。
注意事项
断言Predicate配置
注意:此应用的spring文件最好用yaml来配置,配置的重点就是uri 和 predicates 正常来讲是一一对应的,比如下面配置中,最终访问地址是uri+path,但Path不能随便写,比如把/api去掉,如果去掉了就不能访问到真实地址了,而且后台好像也有一个验证的过程,最恶心的是一直提示404。
yml
gateway:
routes:
- id: path_route
uri: http://localhost:18096
predicates:
- Path=/api/load/v1/**
断言Predicate类别说明
- After:匹配在指定日期时间之后发生的请求。
- Before:匹配在指定日期之前发生的请求。
- Between:需要指定两个日期参数,设定一个时间区间,匹配此时间区间内的请求。
- Cookie:需要指定两个参数,分别为name和regexp(正则表达式),也可以理解Key和Value,匹配具有给定名称且其值与正则表达式匹配的Cookie。
- Header:需要两个参数header和regexp(正则表达式),也可以理解为Key和Value,匹配请求携带信息。
- Host:匹配当前请求是否来自于设置的主机。
- Method:可以设置一个或多个参数,匹配HTTP请求,比如GET、POST
- Path:匹配指定路径下的请求,可以是多个用逗号分隔
- Query:需要指定一个或者多个参数,一个必须参数和一个可选的正则表达式,匹配请求中是否包含第一个参数,如果有两个参数,则匹配请求中第一个参数的值是否符合正则表达式。
- RemoteAddr:匹配指定IP或IP段,符合条件转发。
- Weight:需要两个参数group和weight(int),实现了路由权重功能,按照路由权重选择同一个分组中的路由