办公问答网

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 85|回复: 0

手把手快速入门springcloud之Gateway网关

[复制链接]

3

主题

8

帖子

14

积分

新手上路

Rank: 1

积分
14
发表于 2023-3-22 10:08:41 | 显示全部楼层 |阅读模式
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' 这个时候我们启动服务之后发现服务启动日志: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("path_route2", r -> r.path("/user/getByUsername")
                         .uri("http://localhost:8201/user/getByUsername"))
                 .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 "success";
     }
     //更新路由
     public String update(RouteDefinition definition) {
         try {
             delete(definition.getId());
         } catch (Exception e) {
             return "update fail,not find route  routeId: "+definition.getId();
         }
         try {
             routeDefinitionWriter.save(Mono.just(definition)).subscribe();
             this.publisher.publishEvent(new RefreshRoutesEvent(this));
             return "success";
         } catch (Exception e) {
             return "update route  fail";
         }
     }
     //删除路由
     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("/changeRoute")
public class ChangeRouteController {
     @Autowired
     private GoRouteServiceImpl goRouteServiceImpl;
     //增加路由
     @PostMapping("/add")
     public String add(@RequestBody GatewayRouteDefinition gwdefinition) {
         String flag = "fail";
         try {
             RouteDefinition definition = assembleRouteDefinition(gwdefinition);
             flag = this.goRouteService.add(definition);
         } catch (Exception e) {
             e.printStackTrace();
         }
         return flag;
     }
     //删除路由
     @DeleteMapping("/routes/{id}")
     public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
         try {
             return this.goRouteService.delete(id);
         }catch (Exception e){
             e.printStackTrace();
         }
         return null;
     }
     //更新路由
     @PostMapping("/update")
     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("http")){
             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("Authorization");
         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可能更适合你的需求
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|办公问答网

GMT+8, 2025-3-15 19:24 , Processed in 0.083847 second(s), 22 queries .

Powered by Discuz! X3.4

© 2001-2013 Comsenz Inc. Templated By 【未来科技 www.veikei.com】设计

快速回复 返回顶部 返回列表