什么是网关
网关是用户管理微服务访问问题。可以通过不同的规则路由到不同的微服务。
为什么采用网关
微服务的地址可能存在变化,需要有一个可以统一管理微服务地址的功能,
网关有什么功能
路由(只用了这一部分),认证,鉴权,熔断,限流,日志监控…
必要术语
路由:用于对不同的服务进行转发(真正意思:一个合集包含Id,url,predicates,filters)
断言:对请求进行判断是否应该进该路由。(org.springframework.cloud.gateway.handler.predicate)
过滤:对请求进行修改和添加等操作
过滤器
● 根据生命周期分为pre和post
a. pre为在转发给服务的前做的操作
a. post是转发后服务回来后所做的操作
pre代码模板
@Component
public class AuthorizeGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthorizeGatewayFilterFactory.Config> {
private static final String AUTHORIZE_TOKEN = "token";
//构造函数,加载Config
public AuthorizeGatewayFilterFactory() {
//固定写法
super(Config.class);
System.out.println("Loaded GatewayFilterFactory [Authorize]");
}
//读取配置文件中的参数 赋值到 配置类中
@Override
public List<String> shortcutFieldOrder() {
//Config.enabled
return Arrays.asList("enabled","name");
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
//判断是否开启授权验证
if (!config.isEnabled()) {
return chain.filter(exchange);
}
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
//从请求头中获取token
String token = headers.getFirst(AUTHORIZE_TOKEN);
if (token == null) {
//从请求头参数中获取token
token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
}
ServerHttpResponse response = exchange.getResponse();
//如果token为空,直接返回401,未授权
if (StringUtils.isEmpty(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//处理完成,直接拦截,不再进行下去
return response.setComplete();
}
/**
* todo chain.filter(exchange) 之前的都是过滤器的前置处理
*
* chain.filter().then(
* 过滤器的后置处理...........
* )
*/
//授权正常,继续下一个过滤器链的调用
return chain.filter(exchange);
};
}
public static class Config {
// 控制是否开启认证
private boolean enabled;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Config(boolean enabled, String name) {
this.enabled = enabled;
this.name = name;
}
public Config() {
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}
post过滤器模板
@Component
public class AccessLogGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//filter的前置处理
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().pathWithinApplication().value();
InetSocketAddress remoteAddress = request.getRemoteAddress();
return chain
//继续调用filter
.filter(exchange)
//filter的后置处理
.then(Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
HttpStatus statusCode = response.getStatusCode();
System.out.println("------test---------"+new Date());
// log.info(“请求路径:{},远程IP地址:{},响应码:{}”, path, remoteAddress, statusCode);
}));
}
@Override
public int getOrder() {
return -1;
}
}
● 按照作用域可分为局部过滤域和全局过滤器。
两者的区别:
○ 局部过滤器需要在配置文件中配置方可生效,而全局过滤器不需要。
○ 局部只对声明了的路由进行过滤,全局过滤器只要注册了对所有的路由进行过滤
使用GateWay过滤
是通过过滤器实现限流的:计数器,漏桶,令牌漏桶
RequestRateLimiterGatewayFilterFactory 整体的实现代码
原理是:通过配置KeyResolver来作为redis中的key当执行一次就会往redis进行插入操作,用redis来判断是否超流。
1.实现KeyResolver接口
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
public class IpKeyResolver implements KeyResolver {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
//用于控制key的值
return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
}
2.注入到容器中
@Bean(name = “ipKeyResolver”)
public KeyResolver userIpKeyResolver() {
return new IpKeyResolver();
}
3.配置过滤器
filters:
- StripPrefix=1
# 指定过滤器
- name: RequestRateLimiter
args:
# 指定限流标识
key-resolver: ‘#{@ipKeyResolver}’
# 速率限流
redis-rate-limiter.replenishRate: 1
# 能容纳的并发流量总数
redis-rate-limiter.burstCapacity: 2
踩坑集
当使用nacos+gateway访问时出现503的错误:原因是无法根据lb去识别
org.springframework.cloud
spring-cloud-loadbalancer