| 
 | 
 
Spring Cloud Gateway是Spring生态系统中的一个API网关,它可以处理HTTP请求和响应,并充当微服务架构中的入口点。它是一个基于Spring Framework 5和Spring Boot 2.x的开源项目,采用响应式编程模型,旨在提供高性能、高可靠性和易于使用的API网关解决方案。 
 
工作原理 
 
 
- 请求路由:当一个客户端发送请求到Spring Cloud Gateway时,Gateway会将请求路由到适当的微服务实例。Gateway会根据请求的URI和HTTP方法来匹配预定义的路由规则,并将请求转发给目标微服务。
 
 - 过滤器:Gateway还支持过滤器,开发人员可以编写过滤器来实现请求和响应的定制逻辑,例如鉴权、限流、日志记录等。
 
 - 负载均衡:Gateway可以通过Spring Cloud的负载均衡器来负载均衡请求,这可以确保请求被均匀分配到各个微服务实例中,从而提高系统的可用性和性能。
 
 - 响应转换:Gateway还支持响应转换,可以将微服务返回的响应转换成另一种格式,例如JSON或XML。
 
 - 故障转移:Gateway还支持故障转移,当微服务实例出现故障时,Gateway会自动将请求转发到备用实例,以确保系统的可用性。
 
  GateWay快速开始 
 
引入依赖 
     <dependency> 
         <groupId>org.springframework.cloud</groupId> 
         <artifactId>spring-cloud-starter-gateway</artifactId> 
         <version>2.0.4.RELEASE</version> 
     </dependency>路由的组成 
 
Route(路由):路由是构建网关的基本模块,它由路由(ID,目标URI,一系列的断言)和过滤器组成,如果断言为true则匹配该路由; 
 
什么是断言 
 
路由中的断言可以看做是一种规则或条件,用来匹配用户的请求是否符合我们所设定的路由规则。 
在Spring Cloud Gateway中,路由配置可以包含一个或多个断言(Predicate),用于匹配请求是否符合当前路由规则。如果请求匹配成功,Gateway就会将请求路由到目标微服务,否则会返回404 Not Found错误。 
断言通常用于检查HTTP请求的特定属性,例如请求的URI、HTTP方法、请求头、查询参数等。以下是一些常用的断言: 
 
- Path断言:根据请求的URI匹配路由规则,可以使用Ant风格的通配符。
 
 - Method断言:根据HTTP方法匹配路由规则,例如GET、POST、PUT、DELETE等。
 
 - Header断言:根据请求头匹配路由规则,例如Content-Type、User-Agent等。
 
 - Query断言:根据查询参数匹配路由规则,例如id、name等。
 
  添加路由配置 
 
配置路由:gateway配置路由主要有两种方式,1.用yml配置文件,2.写在代码里 
yml配置文件中配置路由 
 spring: 
   cloud: 
     gateway: 
       routes: 
       - predicates: 
             - Path =/gateway/**  #匹配规则 
         uri: http://localhost:8099/getUser    #服务1的访问地址 
         filters: 
             - StripRrefix: 1 #去掉前缀 
  server: 
     port: 8077针对上面配置含义说明: 
 
- uri:目标服务地址,可配置uri和lb://应用服务名称
 
 - predicates:匹配条件,根据规则匹配是否请求该路由
 
 - filters: 过滤规则,这个过滤包含前置过滤和后置过滤,
 
 - StripPrefix=1,表示去掉前缀,即在转发目标url的时候去掉’gateway&#39; 这个时候我们启动服务之后发现服务启动日志:Netty started on port(s): 8077.说明我们服务成功了,并且网关依赖的是nettyserver启动几个服务监听。 我们访问:
 
   curl http://localhost:8077/gateway/getUser在配置正确的情况下将会返回服务返回的结果。使用Java Bean配置 
 
- 添加相关配置类,并配置一个RouteLocator对象:然后使用注入Bean的方式,注入spring
 
   /** 
  * Created by macro on 2019/9/24. 
  */ 
 @Configuration 
 publicclass GatewayConfig { 
  
     @Bean 
     public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { 
         //配置路由详情 
         return builder.routes() 
                 .route(&#34;path_route2&#34;, r -> r.path(&#34;/user/getByUsername&#34;) 
                         .uri(&#34;http://localhost:8201/user/getByUsername&#34;)) 
                 .build(); 
     } 
 }动态路由 
 
了解即可 
gateway在启动网关后将无法修改路由配置,如有新服务要上线,则需要先把网关下线,修改 yml 配置后,再重启网关。 
      这种方式如果在网关上没有优雅停机就会出现服务间断,这无疑是不能被接受的。gateway网关启动时,路由信息默认会加载内存中,路由信息被封装到 RouteDefinition 对象中,我们可以通过RouteDefinitionWriter这类,封装的方法,对路由配置进行增删改查。从而实现动态路由配置。 
 /** 
  * 动态路由服务 
  */ 
 @Service 
 public class GoRouteServiceImpl implements ApplicationEventPublisherAware { 
     @Autowired 
     private RouteDefinitionWriter routeDefinitionWriter; 
     private ApplicationEventPublisher publisher; 
     @Override 
     public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { 
         this.publisher = applicationEventPublisher; 
     } 
     //增加路由 
     public String add(RouteDefinition definition) { 
         routeDefinitionWriter.save(Mono.just(definition)).subscribe(); 
         this.publisher.publishEvent(new RefreshRoutesEvent(this)); 
         return &#34;success&#34;; 
     } 
     //更新路由 
     public String update(RouteDefinition definition) { 
         try { 
             delete(definition.getId()); 
         } catch (Exception e) { 
             return &#34;update fail,not find route  routeId: &#34;+definition.getId(); 
         } 
         try { 
             routeDefinitionWriter.save(Mono.just(definition)).subscribe(); 
             this.publisher.publishEvent(new RefreshRoutesEvent(this)); 
             return &#34;success&#34;; 
         } catch (Exception e) { 
             return &#34;update route  fail&#34;; 
         } 
     } 
     //删除路由 
     public Mono<ResponseEntity<Object>> delete(String id) { 
         return this.routeDefinitionWriter.delete(Mono.just(id)).then(Mono.defer(() -> { 
             return Mono.just(ResponseEntity.ok().build()); 
         })).onErrorResume((t) -> { 
             return t instanceof NotFoundException; 
         }, (t) -> { 
             return Mono.just(ResponseEntity.notFound().build()); 
         }); 
     } 
 }编写 Rest接口,通过这些接口实现动态路由功能. 
 @RestController 
 @RequestMapping(&#34;/changeRoute&#34;) 
 public class ChangeRouteController { 
     @Autowired 
     private GoRouteServiceImpl goRouteServiceImpl; 
     //增加路由 
     @PostMapping(&#34;/add&#34;) 
     public String add(@RequestBody GatewayRouteDefinition gwdefinition) { 
         String flag = &#34;fail&#34;; 
         try { 
             RouteDefinition definition = assembleRouteDefinition(gwdefinition); 
             flag = this.goRouteService.add(definition); 
         } catch (Exception e) { 
             e.printStackTrace(); 
         } 
         return flag; 
     } 
     //删除路由 
     @DeleteMapping(&#34;/routes/{id}&#34;) 
     public Mono<ResponseEntity<Object>> delete(@PathVariable String id) { 
         try { 
             return this.goRouteService.delete(id); 
         }catch (Exception e){ 
             e.printStackTrace(); 
         } 
         return null; 
     } 
     //更新路由 
     @PostMapping(&#34;/update&#34;) 
     public String update(@RequestBody GatewayRouteDefinition gwdefinition) { 
         RouteDefinition definition = assembleRouteDefinition(gwdefinition); 
         return this.goRouteService.update(definition); 
     } 
     //把传递进来的参数转换成路由对象 
     private RouteDefinition assembleRouteDefinition(GatewayRouteDefinition gwdefinition) { 
         RouteDefinition definition = new RouteDefinition(); 
         definition.setId(gwdefinition.getId()); 
         definition.setOrder(gwdefinition.getOrder()); 
         //设置断言 
         List<PredicateDefinition> pdList=new ArrayList<>(); 
         List<GatewayPredicateDefinition> gatewayPredicateDefinitionList=gwdefinition.getPredicates(); 
         for (GatewayPredicateDefinition gpDefinition: gatewayPredicateDefinitionList) { 
             PredicateDefinition predicate = new PredicateDefinition(); 
             predicate.setArgs(gpDefinition.getArgs()); 
             predicate.setName(gpDefinition.getName()); 
             pdList.add(predicate); 
         } 
         definition.setPredicates(pdList); 
         //设置过滤器 
         List<FilterDefinition> filters = new ArrayList(); 
         List<GatewayFilterDefinition> gatewayFilters = gwdefinition.getFilters(); 
         for(GatewayFilterDefinition filterDefinition : gatewayFilters){ 
             FilterDefinition filter = new FilterDefinition(); 
             filter.setName(filterDefinition.getName()); 
             filter.setArgs(filterDefinition.getArgs()); 
             filters.add(filter); 
         } 
         definition.setFilters(filters); 
         URI uri = null; 
         if(gwdefinition.getUri().startsWith(&#34;http&#34;)){ 
             uri = UriComponentsBuilder.fromHttpUrl(gwdefinition.getUri()).build().toUri(); 
         }else{ 
             // uri为 lb://consumer-service 时使用下面的方法 
             uri = URI.create(gwdefinition.getUri()); 
         } 
         definition.setUri(uri); 
         return definition; 
     } 
 }其实一般我们很少通过API去调用rest服务去增删路由信息,一般我们主流都是通过集成nacos的config功能动态增添路由。 
 
过滤器 
 
过滤器种类 
 
Spring Cloud Gateway的过滤器是由GatewayFilterFactory来创建的,它们可以拦截、修改、转换、验证和处理请求和响应。Spring Cloud Gateway内置了许多常用的过滤器,包括: 
 
- AddRequestHeader:在请求头中添加指定的键值对;
 
 - AddResponseHeader:在响应头中添加指定的键值对;
 
 - ForwardTo:将请求转发到指定的URI;
 
 - Hystrix:在请求处理失败时执行熔断逻辑;
 
 - RedirectTo:将请求重定向到指定的URI;
 
 - RemoveRequestHeader:从请求头中删除指定的键;
 
 - RemoveResponseHeader:从响应头中删除指定的键;
 
 - RewritePath:将请求路径重写为指定的路径;
 
 - SetPath:设置请求路径为指定的路径;
 
 - SetRequestHeader:设置请求头中指定键的值为指定的值。
 
  开发人员还可以根据自己的需求实现自定义的过滤器,只需要实现GatewayFilterFactory接口并注册到Spring容器中即可。 
 
Spring Cloud Gateway的过滤器可以在配置文件中进行配置,如下所示: 
 spring: 
   cloud: 
     gateway: 
       routes: 
         - id: myroute 
           uri: http://example.org 
           filters: 
             - RewritePath=/foo,/bar这个配置表示,当请求访问/myroute/foo时,网关将请求重写为/http://example.org/bar,然后将请求转发到http://example.org网站。RewritePath过滤器是在请求到达网关之前进行处理的,它会将请求路径中的/foo替换为/bar。 
需要注意的是,Spring Cloud Gateway的过滤器是有顺序的,即过滤器的执行顺序与它们在配置文件中的顺序一致。开发人员可以通过调整过滤器的顺序来实现不同的请求处理逻辑。同时,Spring Cloud Gateway还提供了灵活的断言(Predicate)机制,用于匹配请求的路径、参数、请求头等,可以实现更加复杂的请求处理逻辑。 
 
过滤器的顺序 
 
在Spring Cloud Gateway中,过滤器的执行顺序是非常重要的,因为过滤器的执行顺序会影响到请求处理的结果。Spring Cloud Gateway中的过滤器执行顺序如下: 
 
- 前置过滤器(Pre filters):在路由之前执行,可以做一些请求参数的校验、安全认证等工作。
 
 - 路由过滤器(Route filters):用于请求转发,实现动态路由、负载均衡、限流等功能。
 
 - 后置过滤器(Post filters):在路由之后执行,可以对响应结果进行处理,如响应头信息的处理等。
 
 - 错误过滤器(Error filters):当上述过滤器出现异常时会执行该过滤器,用于统一处理异常信息并返回友好的错误信息给客户端。
 
 - 最后才是自定义过滤器(自定义过滤器的执行顺序,再遵循使用@Order定义的优先级)
 
  默认情况下,Spring Cloud Gateway中的过滤器按照上述顺序执行,定义的前置过滤器和后置过滤器具有预定义的顺序,并且在任何使用@Order注解指定的优先级值之前执行即先执行前置过滤器,再执行路由过滤器,然后是后置过滤器,最后是错误过滤器,在最后最后才是我们通过@Order设置的自定义过滤器。当然,也可以通过@Order注解或者OrderedGatewayFilter来控制过滤器的执行顺序。但是需要注意的是,过滤器的执行顺序会对请求处理结果产生影响,所以要谨慎地选择和控制过滤器的执行顺序。 
 
使用@Order定义过滤器执行顺序 
标准级越高,越优先执行 
使用@Order注解定义执行顺序,其是有参考标准级的,标准优先级为0,如果没有指定优先级,则默认为标准优先级。 
其中负数标准高于标准级,高于正数标准级:其中@Order(-1)比较高,可以优先执行,其次是@Order(0)标准标准级,再者才是@Order(+1)正数的标准级 
 
自定义过滤器 
 
实现自定义过滤器只需要两步 
 
- 定义一个配置类,实现过滤器工厂类GatewayFilterFactory接口
 
 - 在yaml配置里面配置对应的过滤器路由即可
 
  Gateway 是一个基于 Spring Boot 的 API 网关服务,它可以处理所有进入应用程序的请求,并对这些请求进行路由、监控、安全性等处理。 
Gateway 允许我们通过自定义过滤器来实现一些自定义的业务逻辑,下面是一个示例: 
 javaCopy code@Component 
 public class CustomFilter implements GatewayFilter, Ordered { 
  
     @Override 
     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { 
         // 在这里可以编写自己的逻辑,例如获取请求头、参数等信息进行校验、处理等操作 
         ServerHttpRequest request = exchange.getRequest(); 
         HttpHeaders headers = request.getHeaders(); 
         String token = headers.getFirst(&#34;Authorization&#34;); 
         if (token == null || token.isEmpty()) { 
             ServerHttpResponse response = exchange.getResponse(); 
             response.setStatusCode(HttpStatus.UNAUTHORIZED); 
             return response.setComplete(); 
         } 
  
         // 继续执行过滤器链 
         return chain.filter(exchange); 
     } 
  
     @Override 
     public int getOrder() { 
         // 设置过滤器的执行顺序,数值越小越先执行 
         return -1; 
     } 
 }上述示例中,我们创建了一个名为 CustomFilter 的过滤器类,实现了 GatewayFilter 接口和 Ordered 接口。其中,filter 方法中编写了自定义逻辑,如果请求头中没有包含 Authorization 参数,则返回未授权的错误响应。如果有该参数,就会继续执行过滤器链。 
在 getOrder 方法中,我们设置了该过滤器的执行顺序为 -1,这意味着该过滤器会在所有内置过滤器之前执行。 
接下来,在配置文件中配置该过滤器: 
 spring: 
   cloud: 
     gateway: 
       routes:   #配置自定义的过滤器路由 
         - id: myroute  # 定义了一个名为myroute的路由规则。id属性用于指定该路由规则的唯一标识 
           uri: http://example.org   # 指定了该路由规则要转发的目标URL 
           filters:   #定义的过滤器 
             - name: AddHeader  #指定了要使用的过滤器的名称 
               args: #定义了过滤器的参数 
                 name: X-MyHeader  #指定了要添加的请求头的名称 
                 value: Added by Gateway #指定了要添加的请求头的值这个配置表示,在访问myroute路由时,网关会将请求转发到http://example.org,并在请求头中添加一个名为X-MyHeader,值为Added by Gateway的键值对。 
通过这个示例,我们可以看到如何实现一个自定义的过滤器,并将它应用到Spring Cloud Gateway中。需要注意的是,在使用GatewayFilterFactory时,我们需要在配置文件中为过滤器指定name,并通过args属性传递配置参数。 
 
Zuul和Gateway的异同 
 
Zuul和Gateway都是用于构建微服务架构中的API网关,它们都有类似的功能,例如路由、负载均衡、安全等。然而,它们有一些不同之处。 
 
- 语言和技术栈:Zuul是Netflix开源的项目,使用Java编写,而Spring Cloud Gateway则是Spring开源的项目,使用Spring Framework 5和Spring Boot 2.x编写。
 
 - 响应式编程:Spring Cloud Gateway采用响应式编程模型,这使得它更加适合处理高流量和高并发的情况。相比之下,Zuul则采用传统的Servlet模型。
 
 - 性能:由于Spring Cloud Gateway采用了响应式编程模型,它的性能比Zuul更好。Spring Cloud Gateway支持异步IO,可以处理更多的请求,而Zuul则是同步处理请求的。
 
 - 易用性:Spring Cloud Gateway比Zuul更易于使用。Spring Cloud Gateway的API设计更加简洁明了,而且有更好的文档和示例。
 
 - 可扩展性:Zuul具有更好的可扩展性,因为它支持插件和过滤器。这使得开发人员可以自定义和扩展Zuul的功能。Spring Cloud Gateway也可以扩展,但是它的扩展点比Zuul少。
 
  总体来说,如果你需要高性能Spring Cloud Gateway是更好的选择。但是,如果你需要更高的可扩展性,那么Zuul可能更适合你的需求。 |   
 
 
 
 |