gateway网关功能:服务路由、负载均衡、请求限流、跨域问题统一解决配置、身份认证和权限校验等
下面记录一下gateway网关的基本功能和配置。PS:没有使用注册中心
目录
1、添加依赖
主要依赖
<!-- SpringCloud gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- gateway基于Redis实现限流 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
完整依赖
<?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">
<!-- 声明依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zhh</groupId>
<artifactId>gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gateway</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
</properties>
<dependencies>
<!-- SpringCloud gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- gateway基于Redis实现限流,如果不需要限流功能,就不需要该依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- SpringBoot相关依赖管理,上面parent标签已经声明了 spring-boot-starter-parent,这里注释掉-->
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>-->
<!-- SpringCloud 相关依赖管理 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- 编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- 打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<!-- 启动类,主类全路径 -->
<mainClass>com.zhh.gateway.GatewayApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
2、配置文件
完整的配置文件,包含了路由(自带负载均衡)、过滤器、限流
server:
port: 8088
spring:
application:
name: cloud-gateway # 本服务名称
cloud:
gateway:
# 解决跨域问题的配置
globalcors:
add-to-simple-url-handler-mapping: true
cors-configurations:
'[/**]': #拦截的请求
allowedOrigins: "*"#允许跨域的请求
allowedMethods: "*" #运行跨域的请求方式
allowedHeaders: "*" #允许请求中携带的头信息
allowedCredentials: true #是否允许携带cookie
maxAge: 36000 #跨域检测的有效期,单位s,在这个时间段里,对于相同的跨域请求不会再预检
# 路由,如果有注册中心,那就自带负载均衡功能
routes:
- id: svs1 #路由的ID,没有固定规则但要求唯一,建议使用服务名称
uri: http://127.0.0.1:8080 #匹配后提供服务的路由地址
predicates:
- Path=/svs1/** # 断言,路径相匹配的进行路由
filters:
- StripPrefix=1 #移除第x个前缀,127.0.0.1:8088/svs1/api/find > 127.0.0.1:8088/api/find
#- PrefixPath=/demo #添加前缀 127.0.0.1:8088/api/find > 127.0.0.1:8088/demo/api/find
# 局部过滤器
- name: Order # 自定义的局部过滤器,前缀匹配,过滤器必须是GatewayFilterFactory结尾
args:
param1: 6688 # 过滤器中的参数
# gateway默认的限流过滤器
- name: RequestRateLimiter #过滤器名称
args:
key-resolver: "#{@urlKeyResolver}" #使用SpEL表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象
redis-rate-limiter.replenishRate: 1 # 令牌桶填充的速率,每秒多少个令牌
redis-rate-limiter.burstCapacity: 1 # 令牌桶总容量
# 第二个路由
- id: svs2
uri: http://127.0.0.1:8001
predicates:
- Path=/svs2/**
# Redis参数配置,这里简单配置一下,其他参数使用默认的即可
# 如果不需要限流功能,就不需要相关依赖和配置
redis:
database: 0
host: 127.0.0.1
port: 6379
password:
3、限流配置
package com.zhh.gateway.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
/**
* 限流配置
*/
@Slf4j
@Configuration
public class KeyResolverConfig {
// KeyResolver的Bean只能配置一个,其他的注释掉
/**
* 基于url限流
*/
@Bean
public KeyResolver urlKeyResolver() {
log.info("限流Bean初始化,BeanName=urlKeyResolver");
return exchange -> {
String url = exchange.getRequest().getPath().toString();
log.info("基于url限流,url:{}",url);
return Mono.just(url);
};
}
/**
* 基于ip限流
*/
/* @Bean
public KeyResolver ipKeyResolver() {
log.info("限流Bean初始化,BeanName=ipKeyResolver");
return exchange -> {
String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
log.info("基于ip限流,ip:{}",ip);
return Mono.just(ip);
};
}*/
}
对应配置文件中的配置
# gateway默认的限流过滤器
- name: RequestRateLimiter #过滤器名称
args:
key-resolver: "#{@urlKeyResolver}" #使用SpEL表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象
redis-rate-limiter.replenishRate: 1 # 令牌桶填充的速率,每秒多少个令牌
redis-rate-limiter.burstCapacity: 1 # 令牌桶总容量
对应pom文件中的Redis依赖
<!-- gateway基于Redis实现限流 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
gateway默认使用的是令牌桶算法配合Redis实现的限流,如果不使用限流功能,可以不需要引入修改依赖和配置。
注:3.x版本的Redis可能会出现限流无效,最好使用4.x及以上版本。
4、过滤器配置
4.1全局过滤器
全局过滤器写好代码后全局生效,不需要在配置文件中配置。
下例:编写一个全局过滤器,对所有的请求都输出日志
package com.zhh.gateway.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 全局过滤器
*/
@Slf4j
@Component
public class LoggingFilter implements GlobalFilter, Ordered {
/**
* 全局过滤器,打印所有请求的日志
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String uri = exchange.getRequest().getPath().value();
log.info("全局过滤器, URI:{}", uri);
return chain.filter(exchange);
}
/**
* 过滤器执行顺序,值越小越靠前
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
4.2 局部过滤器
局部过滤器只针对特定路由生效,编码后需要在配置文件中配置,哪个路由需要就在哪个路由上加上对应的过滤器名称。
局部过滤器类名称必须是GatewayFilterFactory结尾,例:xxxGatewayFilterFactory
下例:编写一个局部过滤器,在对应的请求头中添加参数
package com.zhh.gateway.filter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
/**
* 局部过滤器
* 哪个路由需要该过滤器就在哪个路由的配置上添加该路由标识
* 局部过滤器类名称必须是GatewayFilterFactory结尾,例:xxxGatewayFilterFactory
*/
@Slf4j
@Component
public class OrderGatewayFilterFactory extends AbstractGatewayFilterFactory<OrderGatewayFilterFactory.Config> {
public OrderGatewayFilterFactory() {
super(Config.class);
log.info("初始化局部过滤器");
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange
.getRequest()
.mutate()
// 在请求头中添加参数和值
.header("flag", config.getParam1())
.build();
log.info("在请求头中添加参数和值:[{}]", config.getParam1());
return chain.filter(exchange.mutate().request(request).build());
};
}
/**
* 读取配置文件中的参数,可配置多个属性
*/
@Data
public static class Config {
private String param1;
private String param2;
}
}
对应配置文件中的配置
filters:
# 局部过滤器
- name: Order # 自定义的局部过滤器,前缀匹配,局部过滤器类名称必须是GatewayFilterFactory结尾,例:xxxGatewayFilterFactory
args:
param1: 6688 # 过滤器中的参数
5、跨域问题统一解决配置
目前很多应用都是前后端分离的架构,但这样相比之前的单应用系统就会带来跨域的问题,如果有多个很多服务,就可以在gateway网关层做统一的跨域解决
在配置文件中添加跨域配置即可
spring:
application:
name: cloud-gateway # 本服务名称
cloud:
gateway:
# 解决跨域问题的配置
globalcors:
add-to-simple-url-handler-mapping: true
cors-configurations:
'[/**]': #拦截的请求
allowedOrigins: "*"#允许跨域的请求
allowedMethods: "*" #运行跨域的请求方式
allowedHeaders: "*" #允许请求中携带的头信息
allowedCredentials: true #是否允许携带cookie
maxAge: 36000 #跨域检测的有效期,单位s,在这个时间段里,对于相同的跨域请求不会再预检