Spring Cloud Gateway
- 一、 简介
- 1.1、名词概念
- 1.2、服务核心
- 1.3、服务架构
- 二、 部署
- 2.1、依赖管理
- 2.2、启动类
- 2.3、配置文件
- 三、 路由
- 3.1、id
- 3.2、uri
- 3.3、predicates
- 3.4、filters
- 四、 跨域
- 4.1、源地址
- 4.2、请求方式
- 五、 过滤器
- 5.1、简介
- 5.2、 配置全局默认过滤器
- 5.3、自定义局部过滤器
- 5.4、 自定义全局过滤器
- 六、 扩展知识
- 6.1、负载均衡和熔断
- 6.2、Gateway的高可用
- 6.3、Gateway与Feign的区别
一、 简介
Spring Cloud Gateway
是 Spring 官网基于Spring 5.0
、Spring Boot 2.0
、Project Reactor
等技术开发的网关服务。Spring Cloud Gateway
基于Filter链提供网关基本功能:安全、监控/埋点、限流等。Spring Cloud Gateway
为微服务架构提供简单、有效且统一的API路由管理方式。Spring Cloud Gateway
是替代 Netflix Zuul 的一套解决方案。
- 路由(route): 路由信息由
一个ID
、一个目的URL
、一组断言工厂
、一组Filter
组成。如果路由断言为 真,说明请求URL和配置路由匹配,就可以通过路由去匹配对应的服务,否则不允通过。 - 断言(Predicate):断言函数输入类型是 Spring 5.0框架 中的
ServerWebExchange
,允许开发者去定义匹配来自于HTTP Request中的任何信息比如请求头和参数。 - 过滤器(Filter): Spring Cloud Gateway中的 Filter 分为两种类型,分别是Gateway Filter和Global Filter,过滤器将会对匹配到的 请求 和 响应 进行修改处理 。
- 网关的核心功能是:过滤器链 和 路由转发 。
Spring Cloud Gateway
组件的 核心 是一系列的 过滤器,通过过滤器链可以将客户端发送的请求转发(路由)到对应的微服务。 网关是加在整个微服务最前沿的防火墙和代理器,隐藏微服务 结点 IP 端口信息, 从而加强对服务端口的安全保护。- 网关本身也是一个微服务,也需要将其注册到服务注册中心。
不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都可经过网关,然后再由网关来实现 鉴权、动态路由 等等操作。
网关就是整个服务的统一入口,来自各个地方请求通过网关之后在进行下一步的调度转发。
二、 部署
在配置好 服务中心,具体服务提供 之后,重新添加一个项目模块,项目名自定义。
网关只需要在服务中心进行注册,然后通过网关服务调取其他的服务接口,完成请求的转发即可。
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>bill-manager-springCloud</artifactId>
<groupId>com.beordie</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gateway</artifactId>
<dependencies>
<!--网关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--中心服务-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
通过 springboot 注解启动,添加服务中心的扫描注解。
@SpringBootApplication
@EnableDiscoveryClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
主要配置相应的 服务中心地址 、路由规则 ,以及涉及 跨域资源请求 的配置。
# 配置端口信息
server:
port: 8086
tomcat:
uri-encoding: UTF-8
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: bill-service-route
# 代理的服务地址
uri: lb://bill-service
# 路由断言,可以配置映射路径
predicates:
- Path=/api/**
filters:
# 添加请求路径的前缀
- StripPrefix=1
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
eureka:
client:
service-url:
defaultZone: HTTP://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true
三、 路由
- id: bill-service-route
表示当前设置路由的标识,可以随便进行定义。
uri: HTTP://127.0.0.1:9091
uri: lb://bill-service
将符合 Path
规则的一切请求,都代理到 uri
参数指定的地址,也就是代理到 http://127.0.0.1:9091 的服务。
在刚才的路由规则中,把路径对应的服务地址写死了!如果同一服务有多个实例的话,这样做显然不合理。
使用 lb://bill-service
这样的形式,通过 lb:
加上服务的名字,也就是在对应服务配置文件中指定的 application-name
,也可以从服务中心注册的服务名找。
- 路由配置中 uri 所用的协议为 lb 时(以
uri: lb://bill-service
为例),gateway 将使用 LoadBalancerClient 把 bill-service 通过 eureka 解析为实际的主机和端口,并进行 ribbon 负载均衡。
- Path=/api/**
定义请求当前路由的 url格式
,也就是说 HTTP://127.0.0.1:8086/api/
下面的所有请求都会朱发到当前路由进行处理。
功能和名字意义相同,就是配置路由的过滤器的一些功能。
- 添加前缀:对请求地址添加前缀路径之后再作为代理的服务地址。
- PrefixPath=/bill
- 去除前缀:将请求地址中路径去除一些前缀路径之后再作为代理的服务地址,数字表示去除几个前缀。
- StripPrefix=1
四、 跨域
在 js 请求访问中,如果访问的地址与当前服务器的 域名
、 ip
或者 端口号
不一致则称为跨域请求。若不解决则不能获取到对应地址的返回结果(能够请求,但是结果会被浏览器所拦截)。
一般网关都是所有微服务的统一入口,必然在被调用的时候会出现跨域问题。
表示对所有访问到网关服务器的请求地址都进行跨域设置。
globalcors:
corsConfigurations:
'[/**]':
指定允许访问的服务器地址,如:http://localhost:10000 也是可以的, 如果使用 * 就表示所有的访问都可以。
allowedOrigins: "*"
指定可以访问的请求方式,可以指定多种。
allowedMethods:
- GET
- POST
- HEAD
五、 过滤器
Gateway 作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作往往是通过网关提供的过滤器来实现的。前面的 路由功能 也是使用过滤器实现的。
Gateway自带过滤器有几十个,常见自带过滤器有:
过滤器名称 | 功能 |
AddRequestHeader | 对匹配上的请求加上Header |
AddRequestParameters | 对匹配上的请求路由添加参数 |
AddResponseHeader | 对从网关返回的响应添加Header |
StripPrefix | 对匹配上的请求路径去除前缀 |
这些自带的过滤器的使用和路由配置时的方法一致,也可以将这些过滤器配置成不只是针对某个路由,而是可以对所有路由生效,也就是配置默认过滤器。
# 默认过滤器,对所有路由都生效
default-filters:
- AddResponseHeader=X-Response-Foo, Bar
- AddResponseHeader=name, beordie
和上面一样的配置完成之后,每次的请求响应都会携带两个请求头信息,对应的名称和参数和配置的一致,可以在浏览器中对 Response Header
进行查看。
- 编写过滤器,需要继承 AbstractGatewayFilterFactory 并且需要定义内部类来完成参数的赋值。
- 过滤器名需要以
GatewayFilterFactory
结尾。
package com.beordie.filters;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class MyParamGatewayFilterFactory extends
AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> {
static final String PARAM_NAME = "param";
public MyParamGatewayFilterFactory() {
super(Config.class);
}
public List<String> shortcutFieldOrder() {
return Arrays.asList(PARAM_NAME);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
//获取请求参数中param对应的参数名 的参数值
ServerHttpRequest request = exchange.getRequest();
if(request.getQueryParams().containsKey(config.param)){
request.getQueryParams().get(config.param).
forEach(value -> System.out.printf("------------局部过滤器--------%s = %s------",
config.param, value));
}
return chain.filter(exchange);
};
}
public static class Config{
//对应在配置过滤器的时候指定的参数名
private String param;
public String getParam() {
return param;
}
public void setParam(String param) {
this.param = param;
}
}
}
然后就可以在配置文件对拦截器进行使用。
- MyParam=name
请求在通过时,过滤去就会试图去查找参数名叫做 name
的携带参数,并且进行控制台的打印操作。
- 全局过滤器不需要在配置文件中进行配置,直接定义将会配置在程序中
- 需要实现
GlobalFilter
,Ordered
两个接口。
package com.beordie.filters;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.web.servlet.filter.OrderedFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @Description
* @Date 2021/8/22 17:21
* @Created 30500
*/
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("--------------全局过滤器MyGlobalFilter------------------");
String token = exchange.getRequest().getHeaders().getFirst("token");
if(StringUtils.isBlank(token)){
//设置响应状态码为未授权
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
// 值越小,全局控制器的执行就越靠前
return 1;
}
}
六、 扩展知识
Gateway中默认就已经集成了 Ribbon
负载均衡和 Hystrix
熔断机制。但是所有的超时策略都是走的默认值,比如熔断超时时间只有1S,很容易就触发了。因此建议手动进行配置.
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000 #服务降级超时时间,默认1S
ribbon:
ConnectTimeout: 1000 # 连接超时时长
ReadTimeout: 2000 # 数据通信超时时长
MaxAutoRetries: 0 # 当前服务器的重试次数
MaxAutoRetriesNextServer: 0 # 重试多少次服务
- 启动多个 Gateway 服务,自动注册到Eureka,形成集群。如果是服务内部访问,访问Gateway,自动负载均衡,没问题。
- 但是,Gateway更多是外部访问,PC端、移动端等。它们无法通过Eureka进行负载均衡,那么该怎么办? 此时, 可以使用其它的服务网关,来对Gateway进行代理。比如:Nginx 。
- Gateway 作为整个应用的流量入口,接收所有的请求,如PC、移动端等,并且将不同的请求转发至不同的处理微服务模块,其作用可视为nginx;大部分情况下用作权限鉴定、服务端流量控制。
- Feign 则是将当前微服务的部分服务接口暴露出来,并且主要用于各个微服务之间的服务调用 。