引入依赖包
<dependencies>
<!-- gateway核心包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 基于eureka的功能 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 基于ribbon的功能 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- 基于redis的限流功能 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis支撑包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>
配置文件
Spring相关配置
#应用配置
server:
port: 8808 #应用端口号
servlet:
context-path: / #应用host名称
spring:
gateway:
discovery:
locator:
enabled: false # 根据注册中心自动注册路由,默认false 重启生效
lowerCaseServiceId: false # 将注册表中的服务名转为小写创建路由,默认false,重启生效
httpclient: #http连接设置
pool:
max-connections: 5000 #最大连接数
# max-life-time: 45000 #最大连接时间
acquire-timeout: 15000 #返回时间
max-idle-time: 10000 #最大空闲时间
application:
name: lizz-gateway #应用名称
redis: #redis配置
database: 6 # Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0
host: 10.2.55.7 # Redis服务器地址
port: 6379 # Redis服务器连接端口
password: # Redis服务器连接密码(默认为空)
max-active: 200 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 0 # 连接池中的最小空闲连接
timeout: 1000 # 连接超时时间(毫秒)
# cluster: 集群
# nodes:
# - 127.0.0.1:6379
# - 127.0.0.1:6380
# - 127.0.0.1:6381
# - 127.0.0.1:6382
#eurela注册中心集群地址
eureka:
instance:
prefer-ip-address: true #当微服务有使用IP进行注册时,优先使用IP进行访问
client:
service-url:
defaultZone: http://10.2.55.100:31000/eureka/,http://10.2.55.100:32000/eureka/
#日志级别
logging:
level:
org:
springframework:
cloud:
gateway: TRACE #开启跟踪日志用于测试
路由配置
注:
- “=”号参数不能有空格,如“StripPrefix=1”不能写成“StripPrefix = 1”。
- 路由匹配从上往下,优先匹配前面的路由。
spring:
cloud:
gateway:
filter:
remove-hop-by-hop:
headers: #默认的移除request中的header
- aaa
- bbb
# httpclient:
# connect-timeout: 1000 # 转发后端服务超时时间
# response-timeout: 3s # 转发后端服务响应超时时间
# discovery:
# locator:
# enabled: true #开启服务注册和发现
# lower-case-service-id: true #注册名转为小写
routes: #路由配置
- id: route_1 #直接跳转到指定域名下 http://10.2.11.6:8808/gw/1 > http://10.2.11.6:8800/gw/1
uri: http://10.2.11.6:8800 #目标路径
predicates: #谓词匹配
- Path=/gw/** #匹配路径
- id: route_2 #去除前缀跳转 http://10.2.11.6:8808/sp/hi/1 > http://10.2.11.6:8800/hi/1
uri: http://10.2.11.6:8800 #目标路径
predicates: #谓词匹配
- Path=/sp/** #匹配路径
- Method=GET #指定方式
filters:
- StripPrefix=1 #去除第一个级前缀
- id: route_3 # 增加前缀 http://10.2.11.6:8808/pp/1 > http://10.2.11.6:8800/hi/1
uri: http://10.2.11.6:8800 #目标路径
predicates: #谓词匹配
- Path=/pp/** #匹配路径
filters:
- StripPrefix=1 #去除第一个级前缀
- PrefixPath=/hi #
- id: route_4 # 重写前缀 http://10.2.11.6:8808/rp/1 > http://10.2.11.6:8800/hi/1
uri: http://10.2.11.6:8800 #目标路径
predicates: #谓词匹配
- Path=/rp/** #匹配路径
filters:
- RewritePath=/rp, /hi
- id: route_5 # 动态重写前缀 http://10.2.11.6:8808/rp1 > http://10.2.11.6:8800/hi/rp1
uri: http://10.2.11.6:8800 #目标路径
predicates: #谓词匹配
- Path=/rp1/** #匹配路径
filters:
- RewritePath=(?<oldPath>^/), /hi$\{oldPath}
- id: route_6 # 指定跳转路径 http://10.2.11.6:8808/setpath > http://10.2.11.6:8800/hi/1
uri: http://10.2.11.6:8800 #目标路径
predicates: #谓词匹配
- Path=/setpath/** #匹配路径
filters:
- SetPath=/hi/1
- id: route_7 # 设置路径,同时使用时,根据前后顺序进行设置,改变顺序结果不同 http://10.2.11.6:8808/allpath > http://10.2.11.6:8800/hi/a
uri: http://10.2.11.6:8800 #目标路径
predicates: #谓词匹配
- Path=/allpath/** #匹配路径
filters:
- PrefixPath=/b # http://10.2.11.6:8808/allpath > http://10.2.11.6:8800/b/allpath
- SetPath=/a # http://10.2.11.6:8800/b/allpath > http://10.2.11.6:8800/a
- RewritePath=(?<oldPath>^/), /hi$\{oldPath} # http://10.2.11.6:8800/b/a > http://10.2.11.6:8800/hi/a
- id: route_8 # 通过eureka访问 http://10.2.11.6:8808/eureka/lb/1 > http://10.2.11.6:8800/lb/1
uri: lb://lizz-eureka-provider #目标路径
predicates: #谓词匹配
- Path=/eureka/** #匹配路径
# metadata:
# connect-timeout: 200 #响应时间 ,当前2.2.0版本暂不支持
# response-timeout: 1000 #链接时间,当前2.2.0版本暂不支持
filters:
- StripPrefix=1 #去除第一个级前缀
- name : RequestSize
args:
maxSize: 500000 #限制请求数据大小,byte,比较的长度String contentLength = request.getHeaders().getFirst("content-length");
- name: Retry #重试过滤器,重试调用lizz-eureka-provider的次数
args:
retries: 1 #重试次数 默认3次
statuses: BAD_GATEWAY #HttpStatus 什么情况下重试,可以是错误码,如:500,502
series: # HttpStatus.重试系列 默认 SERVER_ERROR-5xx,SUCCESSFUL-2xx ,空为异常补重试,与statuses并行使用
exceptions: #异常重试 默认IOException and TimeoutException,空为异常补重试
methods: GET,POST # HttpMethod 重试的方法 默认 GET GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
backoff: #延时重试
firstBackoff: 10ms #首次延时次数
maxBackoff: 50ms # 最大延迟后停止
factor: 2 #重试因子 下一次重试延时为firstBackoff*2*n次
basedOnPreviousValue: false #为true, backoff 计算公式为 prevBackoff * factor.
- id: route_9 # 流量限制
uri: lb://lizz-eureka-provider #目标路径
predicates: #谓词匹配
- Path=/iprate/** #匹配路径
filters:
- StripPrefix=1 #去除第一个级前缀
- name: RequestRateLimiter
args:
redis-rate-limiter: #过期时间=burstCapacity/replenishRate*2s
replenishRate: 3 #每次补充数量
burstCapacity: 1000 #突发容量
requestedTokens: 1 #每次请求消耗几个令牌,可以控制不同频率,默认1
key-resolver: "#{@ipKeyResolver}" # ipKeyResolver 自定义限流柜子
# rate-limiter: "#{@myRateLimiter}" # 使用自定义限流规则
- id: route_10 # 修改header
uri: lb://lizz-eureka-provider #目标路径
predicates: #谓词匹配
- Path=/userrate/** #匹配路径
filters:
- AddRequestHeader=X-Request-Foo,Bar #增加转发请求的header 格式:key,value
- AddRequestParameter=foo,bar #增加转发请求的参数
- AddResponseHeader=X-Request-Foo,Bar #增加返回数据的header
- RemoveRequestHeader=X-Request-Foo #移除转发请求的header
- RemoveResponseHeader=X-Request-Foo #移除返回数据的header
Ribbon相关配置
ribbon:
eureka:
enabled: true #默认开启ribbon,关闭fasle
spring:
cloud:
loadbalancer:
retry:
enabled: true #开启
VEHICLE-CUSTOMER-SERVICE: #对应lb的微服务名称
ribbon:
#BestAvailableRule:选择一个最小的并发请求的server
#AvailabilityFilteringRule:过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值)
#WeightedResponseTimeRule:根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。
#RetryRule:对选定的负载均衡策略机上重试机制。
#RoundRobinRule:roundRobin方式轮询选择server
#RandomRule:随机选择一个server
#ZoneAvoidanceRule:复合判断server所在区域的性能和server的可用性选择server
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #负载策略,默认RoundRobinRule轮训策略
NFLoadBalancerRuleClassName: com.gateway.loadbalancer.MatchRule #自定义负载策略
#listOfServers: localhost:8090,localhost:9092,localhost:9999 #自定义服务列表,不使用eureka,需要ribbon.eureka.enabled设置为false
#ServerListRefreshInterval: 10000 #listOfServers列表刷新时间,单位ms
# ReadTimeout: 1 # 请求处理的超时时间
# ConnectTimeout: 1 # 请求连接的超时时间
# OkToRetryOnAllOperations: false # 对所有操作请求都进行重试,默认 false
# MaxAutoRetriesNextServer: 1 #切换重试次数
# MaxAutoRetries: 3 #对当前实例的重试次数
限流规则代码
方法名与配置文件中的key-resolver参数对应
@Configuration
public class RateResolver {
@Value("")
/**
* 必须有一个主键方法
* 根据ip进行限流
* @return
*/
@Primary
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
/**
* 根据用户限流
* 根据参数中的userId值进行限流
* @return
*/
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}
/**
* 根据api限流
* 根据请求path进行限流
* @return
*/
@Bean
KeyResolver apiKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
/**
* 根据自定义key限流
* @return
*/
@Bean
public KeyResolver otherKeyResolver() {
return exchange -> Mono.just(getOtherKey(exchange));
}
/**
* 自定义限流key,根据需要生产key
* @param exchange
* @return
*/
private String getOtherKey(ServerWebExchange exchange){
String key;
String userId = exchange.getRequest().getQueryParams().getFirst("uid");
Map aa = exchange.getAttributes();
key = userId + aa.get("group");
return key;
}
}
限流原理
执行限流过程时,Gateway会通过在request_rate_limiter.lua脚本在redis中创建两个key:
- request_rate_limiter.{$key}.timestamp:最近获取令牌时间
- request_rate_limiter.{$key}.tokens:可用令牌数量
注:key的存活时间为路由配置中的 burstCapacity/replenishRate*2s
当burstCapacity:100,replenishRate:2,100/2*2s=100s
当burstCapacity:200,replenishRate:100,200/100*2s=4s
正常情况几秒内就会消失,需要查看key值可以将差值调大查看。
自定义过滤器
公用过滤器
实现GlobalFilter和Ordered即可。
/**
* @Description: 自定义拦截器
* @Auther: lizz
* @Date: 2019/12/26 17:32
*/
@Component
public class JWTFilter implements GlobalFilter, Ordered {
private static final Logger logger = LoggerFactory.getLogger(JWTFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
logger.info(" JWTFilter 过滤");
//获取请求信息进行过滤
// exchange.getApplicationContext();
// exchange.getAttribute("uid");
// exchange.getAttributes();
// exchange.getFormData();
// exchange.getRequest().getQueryParams();
// exchange.getSession();
//拦截请求
//return exchange.getResponse().setComplete();
//执行下一个拦截器
return chain.filter(exchange);
}
@Override
public int getOrder() {
//拦截器优先级,0位最先执行
return 0;
}
}