SpringCloud微服务API网关Gateway的使用和配置(一)路由转发、断言谓词:https://blog.csdn.net/DreamsArchitects/article/details/119330223
本章学习一下Gateway
的过滤器的用法。
所有的过滤器都是GatewayFilter
的子类,也是工厂模式。和谓词(AbstractRoutePredicateFactory
)是类似的。
AbstractGatewayFilterFactory
下的子类:
上面的XxxxxGatewayFilterFactory
类的前缀就是filter
的属性配置
filters:
- StripPrefix=1
# StripPrefix完整配置
# - name: StripPrefix
# args:
# parts: 1
通过/
来分割路径,去掉部分的路径,然后再调用chain.filter
请求转发微服务:
filters:
- StripPrefix=1 # 去除第一个路径
- PrefixPath=/storage # 请求路径添加前缀
AddRequestHeader: 为我们的请求添加一个请求头
filters:
- StripPrefix=1 # 将请求中的第一个路径去掉 请求路径以/区分,一个/表示一个路径,如:/api/account 会变成/account
#- PrefixPath=/brand # 为请求路径添加前缀/brand
- AddRequestHeader=myheader,admin123
# AddRequestHeader完整写法:
- name: AddRequestHeader
args:
name: newheader
value: admin456
例如:
我们在Account服务中定义一个接口:
接收请求头,并返回,然后我们在浏览器中访问网关请求路由转发到Account服务:
@GetMapping("/findAllAndHeader")
public ResultObject findAllAndHeader(@RequestHeader("myheader") String myheader,@RequestHeader("newheader") String newheader){
System.out.println("myheader:"+myheader);
System.out.println("newheader:"+newheader);
return new ResultObject(true, StatusCode.OK, SystemConstants.QUERY_SUCCESS,myheader+","+newheader);
}
AddRequestParameter:添加一个请求参数
# AddRequestParameter 添加请求参数
- AddRequestParameter=name,lisi
- name: AddRequestParameter
args:
name: age
value: 22
@GetMapping("/findAllAndHeader")
public ResultObject findAllAndHeader(@RequestHeader("myheader") String myheader,@RequestHeader("newheader") String newheader,String name,Integer age){
System.out.println("myheader:"+myheader);
System.out.println("newheader:"+newheader);
System.out.println("name:"+name);
System.out.println("age:"+age);
return new ResultObject(true, StatusCode.OK, SystemConstants.QUERY_SUCCESS,myheader+","+newheader+","+name+","+age);
}
AddResponseHeader:添加一个响应头
DedupeResponseHeader:对指定响应头去除重复
语法:DedupeResponseHeader=响应头名称 响应头参数,strategy
可选的strategy值:
其他不再一一演示
RequestRateLimiter
是基于 Redis
和 Lua
脚本实现的令牌桶算法。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
filters:
- name:
args:
keyResolver: '#{@myResolver}' #使用Spring EL表达式 从容器中获取对象 '#{@beanName}'
redis-rate-limiter.replenishRate: 1 # 生产令牌速度 每秒多少个令牌
redis-rate-limiter.burstCapacity: 5 # 令牌桶容量
根据IP限流:
@Component
public class MyResolver implements KeyResolver {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
InetSocketAddress remoteAddress = exchange.getRequest().getRemoteAddress();
System.out.println("----remoteAddress : "+remoteAddress);
InetAddress address = remoteAddress.getAddress();
System.out.println("address : "+address);
String hostAddress = address.getHostAddress();
System.out.println("hostAddress : "+hostAddress);
return Mono.just(hostAddress);
}
}
配置过滤器:
- name: Hystrix
args:
name: fallback
fallbackUri: forward:/gatewayHystrix #远程服务调用错误的时候 Gateway工程中的哪一个控制器逻辑,返回降级结果
@RestController
public class GatewayController {
@GetMapping("/test")
public Map testApi() throws InterruptedException {
HashMap<String, String> map = new HashMap<>();
map.put("code","0000");
map.put("msg","success");
map.put("api","/api/test");
Thread.sleep(2000);
return map;
}
@RequestMapping(value = "/gatewayHystrix",produces = "text/html;charset=UTF-8")
public String hystrix(){
System.out.println("执行Gateway降级方法");
String body = "服务器繁忙,请稍后再试! ";
return body;
}
}
使用GlobalFilter
实现统一的权限验证、日志记录等希望所有代理的项目都生效的内容都可以配置在全局过滤器中。
package com.lsh.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.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.Date;
/**
* @author :LiuShihao
* @date :Created in 2021/7/23 4:47 下午
* @desc :当请求与路由匹配时,过滤 Web 处理程序会将 的所有实例GlobalFilter和所有特定GatewayFilter于路由的实例添加到过滤器链中。
* 这个组合过滤器链是按org.springframework.core.Ordered接口排序的,你可以通过实现getOrder()方法来设置。
*/
@Slf4j
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//前置过滤
URI uri = exchange.getRequest().getURI();
log.info("Gateway Global Filter : "+uri+" ; date : "+new Date().toString());
Mono<Void> filter = chain.filter(exchange);
log.info("全局过滤器-后置过滤");
//后置过滤
return filter;
}
@Override
public int getOrder() {
return -1;
}
}
参考StripPrefixGatewayFilterFactory
过滤器:
新建一个XxxGatewayFilterFactory
继承AbstractGatewayFilterFactory
:
需要注意几点:
1.无参构造
2. Config内部类
一定要有get/set方法
3.重写apply(Config config)方法
源码:
package com.lsh.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;
/**
* @author :LiuShihao
* @date :Created in 2021/8/16 5:13 下午
* @desc :自定义路由过滤器 参考StripPrefixGatewayFilterFactory
*/
@Component
public class RequestPathGatewayFilterFactory
extends AbstractGatewayFilterFactory<RequestPathGatewayFilterFactory.Config> {
public RequestPathGatewayFilterFactory() {
super(Config.class);
}
//如果可以简化配置方案,当前方法返回简化配置参数命名列表
// RequestPath=NameValue,PathValue
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("name","path");
}
//过滤逻辑
@Override
public GatewayFilter apply(Config config) {
//匿名内部类
return new GatewayFilter() {
//过滤方法
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getPath().toString();
System.out.println("路由过滤器:本次请求地址为:"+path+",配置的参数为:name:"+config.getName()+",path:"+config.getPath());
//写在chain.filter(exchange)之前的前置过滤,之后的后置过滤
Mono<Void> filter = chain.filter(exchange);
return filter;
}
};
}
//内部类
//当前过滤器需要使用的配置内容
public static class Config{
private String name;
private String path;
//Get / Set 方法
public String getName() {
return name;
}
public Config setName(String name) {
this.name = name;
return this;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
}
https://gitee.com/L1692312138/spring-cloud-alibaba