需要父工程引入springcloud的坐标
<dependencies>
<!--springcloudgateway的内部是通过netty+ webfliux实现的,webflux实现和springmvc存在冲突 所以spring-boot-starter-web这个依赖不可以存在于gateway这个工程下,也就是不能继承过来->
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
dependencies>
启动类就是正常的启动类,不需要额外添加注解
server:
port: 8080
spring:
application:
name: api-gateway-server # 服务名称
#配置Spring Cloud Gateway的路由
cloud:
gateway:
routes:
#配置路由: 路由id,路由到微服务的uri,断言(判断条件)
- id: product-service # 保持唯一
uri: http://127.0.0.1:9001 # 目标微服务请求地址
predicates:
- path=/product/** # 路径匹配原则
#访问127.0.0.1/product/1会被转发到http://127.0.0.1:9001/product/1这个url上
#这个gateway其实也是需要注册到eureka上的,在这里写注册到eureka上的配置
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
exclusion>
exclusions>
# 路由断言 xxx时间之后匹配
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://xxxx.com
#路由断言之后匹配 在该日期时间之后发生的请求都将被匹配。
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
# 路由断言 xxx时间之前匹配
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://xxxx.com
#路由断言之前匹配 在该日期时间之前发生的请求都将被匹配。
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
# 路由断言 xxx时间与xxx时间之间匹配
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://xxxx.com
#路由断言之前匹配 在该日期时间之前发生的请求都将被匹配。
predicates:
# 两个时间中间用,分隔
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
# cookie路由断言
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://xxxx.com
#Cookie 路由断言有两个参数,cookie名称和正则表达式。请求包含次cookie名称且正则表达式为真的将会被匹配。
predicates:
# 两个时间中间用,分隔
- Cookie=chocolate, ch.p
# Header 路由断言
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://xxxx.com
#Header 路由断言 Factory有两个参数,header名称和正则表达式。请求包含次header名称且正则表达式为真的将会被匹配。
predicates:
# 两个时间中间用,分隔
- Header=X-Request-Id, \d+
# Host 路由断言
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://xxxx.com
#Host 路由断言 Factory包括一个参数:host name列表。使用Ant路径匹配规则, ,作为分隔符。
predicates:
# 两个时间中间用,分隔
- Host=**.somehost.org,**.anotherhost.org
# Method 路由断言
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://xxxx.com
#Method 路由断言 Factory只包含一个参数: 需要匹配的HTTP请求方式
predicates:
# 两个时间中间用,分隔
- Method=GET
# Path 路由断言
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://xxxx.com
#Method 路由断言 Factory只包含一个参数: 需要匹配的HTTP请求方式
predicates:
# Path 路由断言 Factory 有2个参数: 一个Spring PathMatcher表达式列表和可选matchOptionalTrailingSeparator标识 .
- Path=/foo/{segment},/bar/{segment}
# 例如: /foo/1 or /foo/bar or /bar/baz的请求都将被匹配 URI 模板变量 (如上例中的 segment ) 将以Map的方式保存于ServerWebExchange.getAttributes() key为ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE. 这些值将在GatewayFilter Factories使用
# 可以在Filter中 使用以下方法来更方便地访问这些变量。
Map, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);
String segment = uriVariables.get("segment");
# Query 路由断言
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://xxxx.com
#Method 路由断言 Factory只包含一个参数: 需要匹配的HTTP请求方式
predicates:
# Query 路由断言 Factory 有2个参数: 必选项 param 和可选项 regexp.
- Query=baz
# 包含了请求参数 baz的都将被匹配。
- Query=foo, ba.
# 如果请求参数里包含foo参数,并且值匹配为ba. 正则表达式,则将会被路由,如:bar and baz
# RemoteAddr 路由断言
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://xxxx.com
#Method 路由断言 Factory只包含一个参数: 需要匹配的HTTP请求方式
predicates:
# RemoteAddr 路由断言 Factory的参数为 一个CIDR符号(IPv4或IPv6)字符串的列表,最小值为1,例如192.168.0.1/16(其中192.168.0.1是IP地址并且16是子网掩码)。
- RemoteAddr=192.168.1.1/24
# 如果请求的remote address 为 192.168.1.10则将被路由
# 如果上一层配置了nginx 可能会有一些变化,具体的百度一下 Gateway的修改远程地址的解析方式
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
@EnableEurekaClient
# 注册中心的配置信息
eureka:
client:
service-url:
defaultZone: http://localhost:9000/eureka/
instance:
prefer-ip-address: true #使用ip地址注册
# 更改gateway配置
server:
port: 8080
spring:
application:
name: api-gateway-server # 服务名称
#配置Spring Cloud Gateway的路由
cloud:
gateway:
routes:
#配置路由: 路由id,路由到微服务的uri,断言(判断条件)
- id: product-service # 保持唯一
uri: lb://service-product(这个是微服务名称) # 根据微服务名称从注册中心中拉取服务请求路径
predicates:
- path=/product/** # 路径匹配原则
需要配置一个路由过滤器器
server:
port: 8080
spring:
application:
name: api-gateway-server # 服务名称
#配置Spring Cloud Gateway的路由
cloud:
gateway:
routes:
#配置路由: 路由id,路由到微服务的uri,断言(判断条件)
- id: product-service # 保持唯一
uri: lb://service-product(这个是微服务名称) # 根据微服务名称从注册中心中拉取服务请求路径
predicates:
- path=/product/** # 路径匹配原则
filters: # 配置路由过滤器 http://localhost:8080/product-service/product/1 --> http://127.0.0.1:9001/product/1
在yml中 $ 写成$\
- RewritePath/product-service/(?>.*),/$\{segmemt}
可以配置自动的根据微服务名称进行路由转发
server:
port: 8080
spring:
application:
name: api-gateway-server # 服务名称
#配置Spring Cloud Gateway的路由
cloud:
gateway:
routes:
#配置路由: 路由id,路由到微服务的uri,断言(判断条件)
- id: product-service # 保持唯一
#uri: lb://service-product(这个是微服务名称) # 根据微服务名称从注册中心中拉取服务请求路径
#predicates:
#- path=/product/** # 路径匹配原则
#filters: # 配置路由过滤器 http://localhost:8080/product-service/product/1 --> http://127.0.0.1:9001/product/1
#在yml中 $ 写成$\
#- RewritePath/product-service/(?.*),/$\{segmemt}
# 配置自动根据微服务名称进行路由转发 http://localhost:8080/{微服务id}/product/1
discovery:
locator:
enable: true #开启根据服务名称自动转发
lower-case-service-id: true # 微服务名称已小写形式出现
局部过滤器,是针对单个路由的过滤器,可以对访问的URL过滤,进行切面处理,在SpringCloudGateway中通过GatewayFilter的形式内置了很多不同类型的局部过滤器,
每个过滤器工厂都对应一个实现类,并且这些类的名称必须以 GatewayFilterFactory 结尾,这是
Spring Cloud Gateway的一个约定,例如 AddRequestHeader 对应的实现类为
AddRequestHeaderGatewayFilterFactory 。对于这些过滤器的使用方式可以参考官方文档
全局过滤器(GlobalFilter)作用于所有路由,Spring Cloud Gateway 定义了Global Filter接口,用户
可以自定义实现自己的Global Filter。通过全局过滤器可以实现对权限的统一校验,安全性验证等功
能,并且全局过滤器也是程序员使用比较多的过滤器。
Spring Cloud Gateway内部也是通过一系列的内置全局过滤器对整个路由转发进行处理如下:
自定义全局过滤器(这个类需要加上注解@Component),需要实现GlobalFilter和Ordered接口并实现他们的方法
filter方法执行过滤器中的业务逻辑
getOrder方法指定过滤器的执行顺序,返回值越小,执行的优先级越高
内置的过滤器已经可以完成大部分的功能,但是对于企业开发的一些业务功能处理,还是需要我们自己
编写过滤器来实现的,那么我们一起通过代码的形式自定义一个过滤器,去完成统一的权限校验。
开发中的鉴权逻辑
/**
* 自定义一个全局过滤器
* 实现 globalfilter , ordered接口
*/
@Component
public class LoginFilter implements GlobalFilter,Ordered {
/**
* 执行过滤器中的业务逻辑
* 对请求参数中的access-token进行判断
* 如果存在此参数:代表已经认证成功
* 如果不存在此参数 : 认证失败.
* ServerWebExchange : 相当于请求和响应的上下文(zuul中的RequestContext)
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("执行了自定义的全局过滤器");
//1.获取请求参数access-token
String token = exchange.getRequest().getQueryParams().getFirst("access-token");
//2.判断是否存在
if(token == null) {
//3.如果不存在 : 认证失败
System.out.println("没有登录");
//设置响应码为未认证
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete(); //请求结束,不往下走了
}
//4.如果存在,继续执行
return chain.filter(exchange); //继续向下执行
}
/**
* 指定过滤器的执行顺序 , 返回值越小,执行优先级越高
*/
@Override
public int getOrder() {
return 0;
}
}
SpringCloudGateway官方提供了基于令牌桶的限流支持,基于其内置的过滤器工厂RequestRateLimiterGatewayFilterFactory实现,在过滤器工厂中是通过Redis和lua脚本结合的方式进行流量控制
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redis-reactiveartifactId>
dependency>
# 添加redis配置文件
server:
port: 8080
spring:
application:
name: api-gateway-server # 服务名称
redis:
host: localhost
pool: 6379
database: 0
#配置Spring Cloud Gateway的路由
cloud:
gateway:
routes:
#配置路由: 路由id,路由到微服务的uri,断言(判断条件)
- id: product-service # 保持唯一
uri: lb://service-product(这个是微服务名称) # 根据微服务名称从注册中心中拉取服务请求路径
predicates:
- path=/product-service/** # 路径匹配原则
filters: # 配置限流过滤器
- name: RequestRateLimiter
args:
#使用SpEL从容器中获取对象
key-resolver: '#{@pathKeyResolver}'
#令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
#令牌桶的上限
redis-rate-limiter.burstCapacity: 3
-RewritePath=/product-service/(?>.*),/$\{segment}
# RequestRateLimiter : 使用限流过滤器,是springcloudgateway提供的
# 参数去 RequestRateLimiter GatewayFilterFactory.class中找
# replenishRate:向令牌桶中填充的速率
# burstCapacity:令牌桶的容量
@Configuration
public class KeyResolverConfiguration{
/**
* 编写基于请求路径的限流规则 //abc
* 基于请求ip 127.0.0.1
* 基于参数的
*/
@Bean
public KeyResolver pathKeyResolver(){
return new keyResolver(){
//ServletWebExchange就是上下文参数,可以得到根据什么进行限流
public Mono<String> resolve(SserverWebExchange exchange){
//根据路径进行限流
return Mono.just(exchange.getRequest().getPath().toString());
}
};
}
//基于请求参数的限流,简单写法.请求abc?userId=1 对参数的限流 请求userId=1的时候只能每秒请求3次,多于3次请求不到(根据令牌桶大小和每秒放入桶内数据个数而定)
@Bean
public KeyResolver userKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}
}
//基于请求ip的限流
@Bean
public KeyResolver ipKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getHeaders().getFirst("X-Forwarded-For"));
}
}
upstream gateway{
server 127.0.0.1:8081;
server 127.0.0.1:8080;
}
server{
listen 80;
server_name localhost;
}
location / {
proxy_pass http://gateway;
}