Gateway工作机制

 

 

 

 

文中内容包含:微服务网关限流10万QPS、跨域、过滤器、令牌桶算法。

在德邦快递的微服务实践中,API网关提供了统一的认证、鉴权、API 监控、熔断、限流、降级等功能。当然,熔断、限流、降级在注册中心也会进行一次。

 https://www.cnblogs.com/doondo/p/15070715.html good

https://www.cnblogs.com/mrxiaobai-wen/p/14268696.html

 

RateLimiter和Java中的信号量(java.util.concurrent.Semaphore)类似,Semaphore通常用于限制并发量.

 

 

 

 

 

 

Gateway网关路由有两种配置方式:
1、在配置文件yml中配置
2、代码中注入RouteLocator的Bean

这两种方式是等价的,建议使用yml配置方式

 

网关跨域配置,网关过滤器编写,网关的令牌桶算法限流【每秒10万QPS】

当client端使用http://localhost:8098/consumer/user/info路径进行请求时,如果根据上述进行配置Gateway会将请求转换为http://localhost:8098/service-consumer/user/info。以此作为前端请求的最终目的地。

- id: box-items-service
uri: lb://box-items-service
predicates:
- Path=/open/items/**
filters:
# 表示截取路径的个数
- StripPrefix=1

 

nginx Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务

 zuul ,Zuul 是 Netflix 出品的一个基于 JVM 路由和服务端的负载均衡器。

spring-cloud-gateway, 是spring 出品的 基于spring 的网关项目,集成断路器,路径重写,性能比Zuul好。

 

 

  • 安全 ,只有网关系统对外进行暴露,微服务可以隐藏在内网,通过防火墙保护。
  • 易于监控。可以在网关收集监控数据并将其推送到外部系统进行分析。
  • 易于认证。可以在网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
  • 减少了客户端与各个微服务之间的交互次数
  • 易于统一授权。

微服务网关就是一个系统,通过暴露该微服务网关系统,方便我们进行相关的鉴权,安全控制,日志统一处理,易于监控的相关功能。

 

 

2.2 微服务网关跨域

 

在启动类GatewayApplication中,加入跨域配置代码如下

 

 
@Bean
public CorsWebFilter corsFilter() {
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedMethod("*");//支持所有方法
    config.addAllowedOrigin("*");//跨域处理 允许所有的域
    config.addAllowedHeader("*");//支持所有请求头

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
    source.registerCorsConfiguration("/**", config);//匹配所有请求

    return new CorsWebFilter(source);
}


三、微服务网关过滤器

 

我们可以通过网关过滤器,实现一些逻辑的处理,比如ip黑白名单拦截、特定地址的拦截等。下面的代码中做了两个过滤器,并且设定的先后顺序。

 在网关微服务中创建IpFilter,无需配置其他,注册到Spring容器即可生效

@Component
public class IpFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        //TODO 设置ip白名单
        System.out.println("ip:"+remoteAddress.getHostName());
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        return 1;
    }
}
在网关微服务中创建UrlFilter,无需配置其他,注册到Spring容器即可生效
@Component
public class UrlFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String url = request.getURI().getPath();
        //TODO 拦截特定URL地址
        System.out.println("url:"+url);
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 2;
    }
}


四、网关限流每秒10万请求

 

​ 我们之前说过,网关可以做很多的事情,比如,限流,当我们的系统 被频繁的请求的时候,就有可能 将系统压垮,所以 为了解决这个问题,需要在每一个微服务中做限流操作,但是如果有了网关,那么就可以在网关系统做限流,因为所有的请求都需要先通过网关系统才能路由到微服务中。

 

 

 

4.2 令牌桶算法 介绍

令牌桶算法是比较常见的限流算法之一,大概描述如下:

1)所有的请求在处理之前都需要拿到一个可用的令牌才会被处理; 2)根据限流大小,设置按照一定的速率往桶里添加令牌; 3)桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝; 4)请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除; 5)令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流

 

 

这个算法的实现,有很多技术,Guava(读音: 瓜哇)是其中之一,redis客户端也有其实现。

 

4.3 网关限流代码实现

 

需求:每个ip地址1秒内只能发送10万请求,多出来的请求返回429错误。

 

(1)spring cloud gateway 默认使用redis的RateLimter限流算法来实现。所以我们要使用首先需要引入redis的依赖

 有pre和post两种方式的filter,分别处理前置逻辑和后置逻辑。客户端的请求先经过pre类型的filter,然后将请求转发到具体的业务服务,收到业务服务的响应之后,再经过post类型的filter处理,最后返回响应到客户端。

 

过滤器执行流程如下,order越大,优先级越低

 

@Slf4j
public class AFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("AFilter前置逻辑");
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.info("AFilter后置逻辑");
        }));
    }
}
 
 /**
     * http://localhost:8100/filter/provider
     * @param builder
     * @return
     */
    @Bean
    public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
        // @formatter:off
        // 可以对比application.yml中关于路由转发的配置
        return builder.routes()
                .route(r -> r.path("/filter/**")
                        .filters(f -> f.stripPrefix(1)
                                .filter(new ElapsedFilter()))
                        .uri("lb://idc-cloud-provider")
                        .order(0)
                        .id("filter")
                )
                .build();
        // @formatter:on
    }

 

 d:我们自定义的路由 ID,保持唯一
uri:目标服务地址
predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该属性包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)


 

 

 

 

 

 https://zhuanlan.zhihu.com/p/299608850

posted @ 2020-12-24 14:57  小蚊子大人KN  阅读(400)  评论(0编辑  收藏  举报