ToB企服应用市场:ToB评测及商务社交产业平台

标题: 微服务网关Gateway实践总结 [打印本页]

作者: 莱莱    时间: 2022-9-16 17:24
标题: 微服务网关Gateway实践总结
有多少请求,被网关截胡;
一、Gateway简介

微服务架构中,网关服务通常提供动态路由,以及流量控制与请求识别等核心能力,在之前的篇幅中有说过Zuul组件的使用流程,但是当下Gateway组件是更常规的选择,下面就围绕Gateway的实践做详细分析;

从架构模式上看,网关不管采用什么技术组件,都是在客户端与业务服务中间提供一层拦截与校验的能力,但是相比较Zuul来说,Gateway提供了更强大的功能和卓越的性能;
基于实践的场景来看,在功能上网关更侧重请求方的合法校验,流量管控,以及IP级别的拦截,从架构层面看,通常需要提供灵活的路由机制,比如灰度,负载均衡的策略等,并基于消息机制,进行系统级的安全通知等;

下面围绕客户端、网关层、门面服务的三个节点,分析Gateway的使用细节,即客户端向网关发出请求,经过网关路由到门面服务处理;
二、动态路由

1、基础概念

路由:作为网关中最核心的能力,从源码结构上看,包括ID、请求URI、断言集合、过滤集合等组成;
  1. public class RouteDefinition {
  2.         private String id;
  3.         private URI uri;
  4.         private List<PredicateDefinition> predicates = new ArrayList<>();
  5.         private List<FilterDefinition> filters = new ArrayList<>();
  6. }
复制代码
断言+过滤:通常在断言中定义请求的匹配规则,在过滤中定义请求的处理动作,结构上看都是名称加参数集合,并且支持快捷的方式配置;
  1. public class PredicateDefinition {
  2.         private String name;
  3.         private Map<String, String> args = new LinkedHashMap<>();
  4. }
  5. public class FilterDefinition {
  6.         private String name;
  7.         private Map<String, String> args = new LinkedHashMap<>();
  8. }
复制代码
2、配置路由

以配置的方式,添加facade服务路由,以路径匹配的方式,如果请求路径错误则断言失败,StripPrefix设置为1,即在过滤中去掉第一个/facade参数;
  1. spring:
  2.   application:
  3.     name: gateway
  4.   cloud:
  5.     gateway:
  6.       routes:
  7.         - id: facade
  8.           uri: http://127.0.0.1:8082
  9.           predicates:
  10.             - Path=/facade/**
  11.           filters:
  12.             - StripPrefix=1
复制代码
执行原理如下:

这里是以配置文件的方式,设置facade服务的路由策略,其中指定了路径方式,在Gateway文档中提供了多种路由样例,比如:Header、Cookie、Method、Query、Host等断言方式;
3、编码方式

基于编码的方式管理路由策略,在Gateway文档同样提供了多种参考样例,如果路由服务少并且固定,配置的方式可以解决,如果路由服务很多,并且需要动态添加,那基于库表方式更适合;
  1. @Configuration
  2. public class GateConfig {
  3.     @Bean
  4.     public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
  5.         return builder.routes()
  6.                 .route("facade",r -> r.path("/facade/**").filters(f -> f.stripPrefix(1))
  7.                 .uri("http://127.0.0.1:8082")).build();
  8.     }
  9. }
复制代码
4、库表加载

在常规的应用中,从库表中读取路由策略是比较常见的方式,定义路由工厂类并实现RouteDefinitionRepository接口,涉及加载、添加、删除三个核心方法,然后基于服务类从库中读取数据转换为RouteDefinition对象即可;
  1. @Component
  2. public class DefRouteFactory implements RouteDefinitionRepository {
  3.     @Resource
  4.     private ConfigRouteService routeService ;
  5.     // 加载
  6.     @Override
  7.     public Flux<RouteDefinition> getRouteDefinitions() {
  8.         return Flux.fromIterable(routeService.getRouteDefinitions());
  9.     }
  10.     // 添加
  11.     @Override
  12.     public Mono<Void> save(Mono<RouteDefinition> route) {
  13.         return route.flatMap(routeDefinition -> { routeService.saveRouter(routeDefinition);
  14.             return Mono.empty();
  15.         });
  16.     }
  17.     // 删除
  18.     @Override
  19.     public Mono<Void> delete(Mono<String> idMono) {
  20.         return idMono.flatMap(routeId -> { routeService.removeRouter(routeId);
  21.             return Mono.empty();
  22.         });
  23.     }
  24. }
复制代码
在源码仓库中采用的就是库表管理的方式,代码逻辑的更多细节可以移步Git参考,此处不再过多粘贴;
三、自定义路由策略

  1. @Component
  2. public class DefCheckRoutePredicateFactory extends AbstractRoutePredicateFactory<DefCheckRoutePredicateFactory.Config> {
  3.     public DefCheckRoutePredicateFactory() {
  4.         super(Config.class);
  5.     }
  6.     @Override
  7.     public Predicate<ServerWebExchange> apply(Config config) {
  8.         return new GatewayPredicate() {
  9.             @Override
  10.             public boolean test(ServerWebExchange serverWebExchange) {
  11.                 log.info("DefCheckRoutePredicateFactory:" + config.getName());
  12.                 return StrUtil.equals("butte",config.getName());
  13.             }
  14.         };
  15.     }
  16.     @Data
  17.     public static class Config { private String name; }
  18.     @Override
  19.     public List<String> shortcutFieldOrder() { return Collections.singletonList("name"); }
  20. }
复制代码
  1. @Component
  2. public class DefHeaderGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
  3.     @Override
  4.     public GatewayFilter apply(AbstractNameValueGatewayFilterFactory.NameValueConfig config) {
  5.         return (exchange, chain) -> {
  6.             log.info("DefHeaderGatewayFilterFactory:"+ config.getName() + "-" + config.getValue());
  7.             return chain.filter(exchange);
  8.         };
  9.     }
  10. }
复制代码
  1. spring:
  2.   cloud:
  3.     gateway:
  4.       routes:
  5.         - id: facade
  6.           uri: http://127.0.0.1:8082
  7.           predicates:
  8.             - Path=/facade/**
  9.             - DefCheck=butte
  10.           filters:
  11.             - StripPrefix=1
  12.             - DefHeader=cicada,smile
复制代码
通常来说,在应用级的系统中都需要进行断言和过滤的策略自定义,以提供业务或者架构层面的支撑,完成更加细致的规则校验,尤其在相同服务多版本并行时,可以更好的管理路由策略,从而避免分支之间的影响;
四、全局过滤器

在路由中采用的过滤是GatewayFilter,实际Gateway中还提供了GlobalFilter全局过滤器,虽然从结构上看十分相似,但是其职责是有本质区别的;
  1. @Component
  2. @Order(1)
  3. public class DefOneGlobalFilter implements GlobalFilter {
  4.     @Override
  5.     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  6.         log.info("request-id:{}",exchange.getRequest().getId()) ;
  7.         return chain.filter(exchange);
  8.     }
  9. }
复制代码
  1. @Component
  2. @Order(2)
  3. public class DefTwoGlobalFilter implements GlobalFilter {
  4.     @Override
  5.     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  6.         log.info("request-uri:{}",exchange.getRequest().getURI()) ;
  7.         return chain.filter(exchange);
  8.     }
  9. }
复制代码
Gateway网关作为微服务架构系统中最先接收请求的一层,可以定义许多策略来保护系统的安全,比如高并发接口的限流,第三方授权验证,遭到恶意攻击时的IP拦截等等,尽量将非法请求在网关中拦截掉,从而保证系统的安全与稳定。
五、参考源码
  1. 应用仓库:
  2. https://gitee.com/cicadasmile/butte-flyer-parent
  3. 组件封装:
  4. https://gitee.com/cicadasmile/butte-frame-parent
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4