前面章节中小猿都采用springcloud来搭建微服务,但是不够完美,消费者和服务提供者之间的调用往往是将地址暴露出来,这就造成安全隐患,为了解决这一隐患,springgateway就出世了。
This project provides a library for building an API Gateway on top of Spring WebFlux. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.
Built on Spring Framework 5, Project Reactor and Spring Boot 2.0.
Able to match routes on any request attribute.
Predicates and filters are specific to routes.
Circuit Breaker integration.
Spring Cloud DiscoveryClient integration.
Easy to write Predicates and Filters.
Request Rate Limiting.
Path Rewriting.
spring cloud gateway 的核心就是一系列的过滤器,可以将客户端的请求转到不同的服务器,即可简要的称为过滤和路由。
路由(route) 路由信息的组成:由一个ID、一个目的URL、一组断言工厂、一组Filter组成。如果路由断言为真,说明请求URL和配置路由匹配。
断言(Predicate) Spring Cloud Gateway中的断言函数输入类型是Spring 5.0框架中的ServerWebExchange。Spring Cloud Gateway的断言函数允许开发者去定义匹配来自于Http Request中的任何信息比如请求头和参数。
过滤器(Filter) 一个标准的Spring WebFilter。 Spring Cloud Gateway中的Filter分为两种类型的Filter,分别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改处理。
<?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>springcloudparents</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springgateway</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>
<build>
<plugins>
<!--<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!--<executions>
<execution>
<phase>none</phase>
</execution>
</executions>-->
</plugin>
</plugins>
</build>
</project>
@EnableDiscoveryClient
@SpringBootApplication
public class GateWayApplication {
public static void main(String[] args) {
SpringApplication.run(GateWayApplication.class, args);
}
}
server:
port: ${port:10010}
spring:
application:
name: apigateway
cloud:
gateway:
routes:
# 路由id,可以任意设置
- id: userserviceroute
uri: http://127.0.0.1:9090
# 路由断言: 可以匹配映射路径,即当路径中包含userController时
#就直接跳转到url: http://127.0.0.1:9090的服务器
predicates:
- Path=/userController/**
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true
其他的模块可以参考小猿的前几篇博文。
本案例中还是存在一个问题,路由的服务对象ip地址写死,我们需要修改uri
uri: lb://userservice
添加前缀:对请求地址添加前缀路径之后再作为代理的服务地址;
spring:
application:
name: apigateway
cloud:
gateway:
routes:
# 路由id,可以任意设置
- id: userserviceroute
#此地址不可以写死
#uri: http://127.0.0.1:9090
uri: lb://userservice
# 路由断言: 可以匹配映射路径,即当路径中包含userController时
#就直接跳转到url: http://127.0.0.1:9090的服务器
predicates:
#- Path=/api/userController/**
- Path=/**
filters:
- PrefixPath=/userController
配置删除前缀
spring:
application:
name: apigateway
cloud:
gateway:
routes:
# 路由id,可以任意设置
- id: userserviceroute
#此地址不可以写死
#uri: http://127.0.0.1:9090
uri: lb://userservice
# 路由断言: 可以匹配映射路径,即当路径中包含userController时
#就直接跳转到url: http://127.0.0.1:9090的服务器
predicates:
- Path=/api/userController/**
# - Path=/**
filters:
#- PrefixPath=/userController
# 1代表去除一个路径
- StripPrefix=1
Route filters allow the modification of the incoming HTTP request or outgoing HTTP response in some manner. Route filters are scoped to a particular route. Spring Cloud Gateway includes many built-in GatewayFilter Factories.
Gateway实现方式上,有两种过滤器;
局部过滤器:通过 spring.cloud.gateway.routes.filters 配置在具体路由下,只作用在当前路由上;自带的过滤器都可以配置或者自定义按照自带过滤器的方式。如果配置
全局过滤器:不需要在配置文件中配置,作用在所有的路由上;实现 GlobalFilter 接口即可。
spring.cloud.gateway.default-filters 上会对所有路由生效也算是全局的过滤器;但是这些过滤器的实现上都是要实现GatewayFilterFactory接口。
局部过滤器使用方法
The AddResponseHeader GatewayFilter Factory,The AddResponseHeader GatewayFilter Factory takes a name and value parameter. The following example configures an AddResponseHeader GatewayFilter:
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
filters:
- AddResponseHeader=X-Response-Red, Blue
上述是spring官网给出添加响应的案例,若采用该类过滤器则仅限于该路由链路 。小猿在此就不做演示,小猿来演示一下全局过滤器。
Default Filters: To add a filter and apply it to all routes, you can use spring.cloud.gateway.default-filters. This property takes a list of filters. The following listing defines a set of default filters:
spring:
application:
name: apigateway
cloud:
gateway:
routes:
# 路由id,可以任意设置
- id: userserviceroute
#此地址不可以写死
#uri: http://127.0.0.1:9090
uri: lb://userservice
# 路由断言: 可以匹配映射路径,即当路径中包含userController时
#就直接跳转到url: http://127.0.0.1:9090的服务器
predicates:
#- Path=/api/userController/**
- Path=/**
filters:
- PrefixPath=/userController
# 1代表去除一个路径
#- StripPrefix=1
#默认过滤器,对所有路由都生效
default-filters:
- AddResponseHeader=X-Response-Default-MyName, xueshanfeitian
- AddResponseHeader=X-Response-Default-MyName, feitian
Spring Cloud Gateway 的 Filter 的生命周期也类似Spring MVC的拦截器有两个:“pre” 和 “post”。“pre”和 “post” 分别会在请求被执行前调用和被执行后调用。 pre 和 post 可以通过过滤器的 GatewayFilterChain 执行filter方法前后来实现。
通过以上对过滤器的分析,我们就可以实现自定义过滤器,自定义过滤器的一个重要应用是获取url中参数的值。
server:
#修改端口高可用
port: ${port:10086}
spring:
application:
name: eureka-server
eureka:
client:
service-url:
# eureka 服务地址,如果是集群的话;需要指定其他集群Eureka地址
# 修改defaultZone高可用
defaultZone: ${defaultZone:http://127.0.0.1:10086/euraka}
#不注册自己 默认为ture
register-with-eureka: false
#不主动拉取服务 默认为ture
fetch-registry: false
server:
#服务失效剔除时间间隔 默认60秒
eviction-interval-timer-in-ms: 30000
#关闭自我保护
enable-self-preservation: false
@Component
public class MyGatewayFilterFactory extends
AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
static final String PARAM_NAME = "param";
public MyGatewayFilterFactory(){
super(Config.class);
}
public List<String> shortcutFieldOrder(){
//没有名称和数值
//return Arrays.asList(GatewayFilter.NAME_KEY,GatewayFilter.VALUE_KEY);
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 Config(String param) {
this.param = param;
}*/
public String getParam() {
return param;
}
public void setParam(String param) {
this.param = param;
}
}
}
模拟一个登录的校验。基本逻辑:如果请求中有token参数,则认为请求有效,放行。
@Component
public class MyGlobalFilter implements GlobalFilter , Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("---------执行全局过滤器-----------");
String token = exchange.getRequest().getQueryParams().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
ribbon:
ConnectTimeout: 1000
ReadTimeout: 2000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 0
跨域是指在js请求访问中,如果访问的地址与当前服务器的域名、ip或者端口号不一致则称为跨域请求。若不解决则不能获取到对应地址的返回结果。
网管是所有微服务的请求入口,当前端发送ajax(axios)请求时则可能会产生跨域问题,下面是一个跨域请求配置:
spring:
application:
name: apigateway
cloud:
gateway:
routes:
# 路由id,可以任意设置
- id: userserviceroute
#此地址不可以写死
#uri: http://127.0.0.1:9090
uri: lb://userservice
# 路由断言: 可以匹配映射路径,即当路径中包含userController时
#就直接跳转到url: http://127.0.0.1:9090的服务器
predicates:
#- Path=/api/userController/**
- Path=/**
filters:
- PrefixPath=/userController
# 1代表去除一个路径
#- StripPrefix=1
- My=name
#默认过滤器,对所有路由都生效
default-filters:
- AddResponseHeader=X-Response-Default-MyName, xueshanfeitian
- AddResponseHeader=X-Response-Default-MyName, feitian
globalcors:
cors-configurations:
#对所有访问到网关服务器的请求地址
'[/**]':
#allowedOrigins: *
allowedOrigins:
# 可以允许来自 http://docs.spring.io 的get请求方式获取服务数据。
- "http://docs.spring.io"
allowedMethod:
- GET
具体的配置含义可以直接查看代码注释。