SpringCloud Gateway 简介
       Spring Cloud Gateway 是由 spring 官方基于 Spring5.0、Spring Boot2.x、Project Reactor 等技术开发的 网关,目的是代替原先版本中的 Spring Cloud Netfilx Zuul。
       这是我在 2019 年做的一个 SpringCloud 的课件,主要目的是培训团队成员。
Gateway 网关特性
统一入口所有请求通过网关路由到内部其他服务。
断言(Predicates)和过滤器(filters)特定路由。断言是根据具体的请求的规则由 route 去处理;过滤器用来对请求做各种判断和修改。
可集成熔断器:Hystrix 熔断机制。Hystrix 是 spring cloud gateway 中是以 filter 的形式使用的。
流量限速:请求限流防止大规模请求对业务数据造成破坏。
路径重写(rewrite):路径重写自定义路由转发规则。
能够自由设置任何请求属性的路由
Spring Cloud DiscoveryClient 原生支持
Gateway 流程架构
静态路由
       所谓静态路由,就是指 API 网关启动前,通过配置文件或者代码的方式,静态的配置好 API 之间的路由关系,此后不需要二次维护,大多数的内部 API 网关适用于这种方式。
方法一 配置文件方式
     本质上是修改application.yml文件,相关修改方法,官网已经有详尽的描述了,如需帮助可参考官方文档。本文仅举例其中一种,一看便知。
 spring:  cloud:    gateway:      routes:      - id: ingredients        uri: lb://ingredients        predicates:        - Path=//ingredients/**        filters:        - name: CircuitBreaker          args:            name: fetchIngredients            fallbackUri: forward:/fallback      - id: ingredients-fallback        uri: http://localhost:9994        predicates:        - Path=/fallback        filters:        - name: FallbackHeaders          args:            executionExceptionTypeHeaderName: Test-Header     - id: ijep-service-sys  #基础服务       uri: lb://ijep-service-sys       predicates:       - Path=/api/sys/**       filters:       - StripPrefix=2
       复制代码
 方法二 代码构建路由
 // static imports from GatewayFilters and RoutePredicates@Beanpublic RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {    return builder.routes()            .route(r -> r.host("**.abc.org").and().path("/image/png")                .filters(f ->                        f.addResponseHeader("X-TestHeader", "foobar"))                .uri("http://httpbin.org:80")            )            .route(r -> r.path("/image/webp")                .filters(f ->                        f.addResponseHeader("X-AnotherHeader", "baz"))                .uri("http://httpbin.org:80")                .metadata("key", "value")            )            .route(r -> r.order(-1)                .host("**.throttle.org").and().path("/get")                .filters(f -> f.filter(throttle.apply(1,                        1,                        10,                        TimeUnit.SECONDS)))                .uri("http://httpbin.org:80")                .metadata("key", "value")            )            .build();}
       复制代码
        通过代码即可完成路由的创建,会在 Spring Cloud Gateway 启动时自动加载到运行态,本质上配置文件和代码方式,仅仅是两种加载形态,底层没有太大的区别。然而,静态路由往往满足不了我们的需求。
断言(Predicates)
官方参考地址https://cloud.spring.io/spring-cloud-gateway/reference/html/#_after_route_predicate_factory
过滤器(Filter)
        路由过滤器允许以某种方式修改传入的 HTTP 请求或传出的 HTTP 响应。路径过滤器的范围限定为特定路径。Spring Cloud Gateway 包含许多内置的 GatewayFilter 工厂。
GlobalFilter 全局过滤器
CORS 跨域处理
例子:对于所有 GET 请求的路径,将允许来自 docs.spring.io 的请求的 CORS 请求。
Gateway API
支持通过接口动态调整网关策略。
/actuator/gateway/refresh
POST
刷新路由缓存
/actuator/gateway/routes
GET
查询路由
/actuator/gateway/globalfilters
GET
查询全局过滤器
/actuator/gateway/routefilters
GET
查询过滤器
/actuator/gateway/routes/{id}
GET、POST、DELETE
查询指定路由信息
无论是哪一种,在启动网关后将无法修改路由配置,如有新服务要上线,则需要先把网关下线,修改 yml 配置后,再重启网关。
动态路由
       Gateway 网关启动时,路由信息默认会加载内存中,路由信息被封装到 RouteDefinition 对象中,配置多个 RouteDefinition 组成 Gateway 的路由系统。
 package org.springframework.cloud.gateway.route; 
  @Validated  public class RouteDefinition {
  @NotEmpty  private String id = UUID.randomUUID().toString();
  @NotEmpty  @Valid  private List<PredicateDefinition> predicates = new ArrayList<>();
  @Valid  private List<FilterDefinition> filters = new ArrayList<>();
  @NotNull  private URI uri;
  private int order = 0;
  public RouteDefinition() {}
	.....此处省略......
}
       复制代码
 RouteDefinition 中的字段与上面代码配置方式比较对应
 package org.springframework.cloud.gateway.route;
import reactor.core.publisher.Flux;
public interface RouteDefinitionLocator {	Flux<RouteDefinition> getRouteDefinitions();}
       复制代码
 
       RouteDefinitionLocator 是个接口,在 org.springframework.cloud.gateway.route 包下,如果想查看网关中所有的路由信息,可以调用此方法;后面还有另外一种查看方式,是 Spring Cloud Gateway 的 Endpoint 端点提供的方法
处。
Gateway Endpoint 端点
       Endpoint 端点有暴露路由信息、获取所有路由、刷新路由、查看单个路由、删除路由等方法,源码在 org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint 中,访问端点中的方法需要修改 pom 和配置文件:
添加 pom 文件依赖:
 <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-actuator</artifactId></dependency>
       复制代码
 yml 配置
 # 暴露端点management: endpoints:   web:    exposure:    include: '*' endpoint:    health:    show-details: always
       复制代码
 动态路由实现
创建路由模型
过滤器模型
 public class GatewayFilterDefinition {//Filter Nameprivate String name;//对应的路由规则private Map<String, String> args = new LinkedHashMap<>();   ......此处省略Get、Set方法......}
       复制代码
 路由断言模型
 public class GatewayPredicateDefinition {  //断言对应的Name  private String name;
  //配置的断言规则  private Map<String, String> args = new LinkedHashMap<>();
   ......此处省略Get、Set方法......  }
       复制代码
 路由模型
 public class GatewayRouteDefinition {
//路由的Idprivate String id;
//路由断言集合配置private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
//路由过滤器集合配置private List<GatewayFilterDefinition> filters = new ArrayList<>();
//路由规则转发的目标uriprivate String uri;
//路由执行的顺序private int order = 0;
......此处省略Get、Set方法......}
       复制代码
 编写态路由实现类,实现 ApplicationEventPublisherAware 接口
 @Servicepublic class DynamicRouteServiceImpl implements ApplicationEventPublisherAware{
    @Autowired    private RouteDefinitionWriter       routeDefinitionWriter;
    private ApplicationEventPublisher   publisher;
  @Override  public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {    this.publisher = applicationEventPublisher;}
/** * 增加路由 * @param definition * @return */  public String add(RouteDefinition definition) {      routeDefinitionWriter.save(Mono.just(definition)).subscribe();      this.publisher.publishEvent(new RefreshRoutesEvent(this));      return "success";  }
/** * 更新路由 * @param definition * @return */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";    }}
/** * 删除路由 * @param id * @return */  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());    });  }}
       复制代码
 编写 API 接口,实现动态路由功能
 @RestController@RequestMapping("/api/route")public class RouteRestController {
   @Autowired   private DynamicRouteServiceImpl dynamicRouteService;
/** * 增加路由 * @param gatewayRouteDefinition 路由模型 *  报文:{"filters":[{"name":"StripPrefix","args":{"_genkey_0":"2"}}],"id":"ijep-service-sys-hyk11","uri":"lb://ijep-service-sys","order":2,"predicates":[{"name":"Path","args":{"_genkey_0":"/api/hyk11/**"}}]} * @return */@PostMapping("/add")public RestResponse add(@RequestBody GatewayRouteDefinition gatewayRouteDefinition) {    String flag = "fail";    try    {        RouteDefinition definition = assembleRouteDefinition(gatewayRouteDefinition);        flag = this.dynamicRouteService.add(definition);    } catch (Exception e) {        e.printStackTrace();    }
    return RestResponseBuilder.restResponse().data(flag).build();}
/** * 更新路由 * @param gatewayRouteDefinition 路由模型 *  报文:{"filters":[{"name":"StripPrefix","args":{"_genkey_0":"2"}}],"id":"ijep-service-sys-hyk11","uri":"lb://ijep-service-sys","order":2,"predicates":[{"name":"Path","args":{"_genkey_0":"/api/hyk11/**"}}]} * @return */@PostMapping("/update")public RestResponse update(@RequestBody GatewayRouteDefinition gatewayRouteDefinition) {
    RouteDefinition definition = assembleRouteDefinition(gatewayRouteDefinition);    String flag = this.dynamicRouteService.update(definition);
    return RestResponseBuilder.restResponse().data(flag).build();}
/** * 删除路由 * @param id 路由Id * @return */@DeleteMapping("/routes/{id}")public RestResponse delete(@PathVariable String id) {
    Mono<ResponseEntity<Object>> responseEntityMono = this.dynamicRouteService.delete(id);    return RestResponseBuilder.restResponse().data(responseEntityMono).build();}
/** * 把传递进来的参数转换成路由对象 * @param gatewayRouteDefinition 路由模型 * @return */private RouteDefinition assembleRouteDefinition(GatewayRouteDefinition gatewayRouteDefinition) {    RouteDefinition definition = new RouteDefinition();    definition.setId(gatewayRouteDefinition.getId());    definition.setOrder(gatewayRouteDefinition.getOrder());
    //设置断言    List<PredicateDefinition> pdList = new ArrayList<>();    List<GatewayPredicateDefinition> gatewayPredicateDefinitionList = gatewayRouteDefinition.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 = gatewayRouteDefinition.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(gatewayRouteDefinition.getUri().startsWith("http")){        uri = UriComponentsBuilder.fromHttpUrl(gatewayRouteDefinition.getUri()).build().toUri();    }else{        // uri为 lb://consumer-service 时使用下面的方法        uri = URI.create(gatewayRouteDefinition.getUri());    }    definition.setUri(uri);    return definition;}}
       复制代码
 新增路由接口
评论