Spring Cloud教程 第十弹 第二代网关spring cloud gateway

本文深入介绍了Spring Cloud Gateway的核心概念,包括路由、断言和过滤器的工作原理。详细讲解了如何配置路由断言如Path、Header等,并通过YAML和JavaConfig展示了配置示例。同时,文章还涵盖了Gateway如何与Eureka服务注册中心集成,实现自动路由匹配。此外,还探讨了自定义GatewayFilter和GlobalFilter的方法,以及如何创建自定义的GatewayFilterFactory。最后,给出了实际的代码示例来展示过滤器的使用。

更多Spring与微服务相关的教程请戳这里 Spring与微服务教程合集

1、核心概念

1.1、路由route

路由是网关最基础的部分。路由信息由一个ID、一个目的url、一组断言工厂和一组Filter组成。如果路由断言为真,则说明请求的url和配置的路由匹配。

1.2、断言predicate

Java8中的断言函数。spring cloud gateway中的断言函数输入类型是spring5.0框架中的ServerWebExchange。spring cloud gateway中的断言函数允许开发者去定义来自于Http Request中的任何信息,比如请求头和参数等。

1.3、过滤器filter

一个标准的spring webfilter。spring cloud gateway中的filter分为两种类型:分别是gateway filter和global filter

2、工作原理

  1. gateway客户端向gateway发起请求
  2. 请求首先被HttpWebHandlerAdapter进行提取组装成网关的上下文
  3. 然后网关上下文会传递到DispatcherHandler
  4. DispatcherHandler是所有请求的分发处理器,主要负责分发请求对应的处理器。比如将请求分发到对应的RoutePredicate-HandlerMapping(路由断言处理映射器)
  5. 路由断言处理映射器主要用于路由的查找,以及找到路由后返回对应的FilterWebHandler
  6. FilterWebHandler主要负责组装Filter链表并调用Filter执行一系列的Filter处理,然后把请求转到后端对应的代理服务处理
  7. 处理完毕后,将response返回给gateway客户端

注意:

  • 过滤器分为Pre和Post,分别在转发请求之前处理和接收到代理服务返回的结果之后处理
  • 在配置路由时,如果不指定端口,则http默认端口为80,https默认端口为443
  • gateway目前只支持netty容器

2、路由断言和过滤

2.1、概述

路由断言:

  • Path路由断言工厂
  • Before路由断言工厂
  • Between断言工厂
  • Cookie路由断言工厂
  • Header路由断言工厂
  • Host路由断言工厂
  • Method路由断言工厂
  • Query路由断言工厂
  • RemoteAddr路由断言工厂

路由过滤:

spring cloud gateway内置了很多的路由过滤工厂,也可以根据需要自定义路由过滤工厂

路由过滤器允许以某种方式修改request或response

2.2、基于yml配置

spring:
  cloud:
    gateway:
      routes:
        - id: findAll
          uri: http://localhost:8130
          predicates:
            - Path=/findAll
        - id: findOne
          uri: http://localhost:8130
          predicates:
            - Path=/findOne

2.3、基于JavaConfig配置

import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.GatewayFilterSpec;
import org.springframework.cloud.gateway.route.builder.PredicateSpec;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.route.builder.UriSpec;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.function.Function;


@Configuration
public class GatewayRouteConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder){
        ZonedDateTime dateTime = ZonedDateTime.of(2020, 10, 17, 22, 40, 0, 0, ZoneId.systemDefault());

        return builder.routes()
                //普通path断言
                .route(r->r.path("/getStudentName")
                        .filters(new Function<GatewayFilterSpec, UriSpec>() {
                            @Override
                            public UriSpec apply(GatewayFilterSpec gatewayFilterSpec) {
                                return gatewayFilterSpec.addRequestHeader("stuName", "bobo");
                            }
                        })
                        .uri("http://localhost:8130")
                        .id("getStudentName"))
                //after断言+path断言
                .route(new Function<PredicateSpec, Route.AsyncBuilder>() {
                    @Override
                    public Route.AsyncBuilder apply(PredicateSpec predicateSpec) {
                        predicateSpec.path("/listStudent").and().after(dateTime);
                        Route.AsyncBuilder asyncBuilder = predicateSpec.uri("http://localhost:8130");
                        asyncBuilder.id("listStudent");
                        return asyncBuilder;
                    }
                })
                //path通配符断言
                .route(r->r.path("/student/**")
                        .uri("http://localhost:8130")
                        .id("student"))
                .build();
    }
}

3、gateway集成eureka服务注册中心

3.1、pom.xml

注意springboot和springcloud的版本,如果是其它版本,启动项目的时候可能会报错

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
   
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR4</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

3.2、application.yml关键配置

spring:
  cloud:
    gateway:
      discovery:
        locator:
          #与服务注册中心集成
          enabled: true
          #因为eureka的serviceId默认是大写,设为true之后,在gateway中可以用小写的serviceId进行路由转发
          lowerCaseServiceId: true
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8000/eureka/
    register-with-eureka: true

3.3、自动路由匹配

gateway与eureka集成后,即使不配置路由,也会进行自动路由匹配

比如在eureka上注册了一个服务提供者service-a,service-a的上下文是/service-a,端口是8080,且暴露了一个接口,路径为/student/getName

gateway的端口是9000,则访问http://localhost:9000/service-a/service-a/student/getName,可自动匹配到service-a

通过gateway的日志可以看到,自动路由的信息如下:

Route{id='ReactiveCompositeDiscoveryClient_SERVICE-A', uri=lb://SERVICE-A, order=0, predicate=Paths: [/service-a/**], \
其中,lb表示该uri从注册中心查找

4、Filter

4.1、概述

spring cloud gateway的Filter实际上分两种:

  • GatewayFilter:应用到单个路由或一组路由,前面讲的路由过滤器工厂就是GatewayFilter
  • GlobalFilter:应用到所有的路由

4.2、自定义GatewayFilter

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class PrintHeaderGatewayFilter implements GatewayFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("执行PrintHeaderFilter");
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        Set<Map.Entry<String, List<String>>> entries = headers.entrySet();
        for (Map.Entry<String, List<String>> entry : entries) {
            System.out.println(entry.getKey()+"----"+entry.getValue());
        }
        return chain.filter(exchange);
    }
}

4.3、自定义GlobalFilter

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;


@Component
public class TokenGlobalFilter implements GlobalFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        MultiValueMap<String, String> queryParams =
                exchange.getRequest().getQueryParams();
        List<String> list = queryParams.get("token");
        if(CollectionUtils.isEmpty(list)){
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }
}

4.4、自定义GatewayFilterFactory

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;


@Component
public class PrintParamGatewayFilterFactory extends AbstractGatewayFilterFactory<PrintParamGatewayFilterFactory.Config>{
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                String url = exchange.getRequest().getURI().getPath();
                if (!config.getIgnoreUrl().contains(url)){
                    MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
                    for (String s : queryParams.keySet()) {
                        System.out.println(s+"----"+queryParams.get(s));
                    }
                }
                return chain.filter(exchange);
            }
        };
    }

    public PrintParamGatewayFilterFactory(){
        super(Config.class);
    }

    /**
     * 自定义config类,能读取到yml中的配置
     */
    public static class Config{
        private List<String> ignoreUrl;

        public List<String> getIgnoreUrl() {
            return ignoreUrl;
        }

        public void setIgnoreUrl(List<String> ignoreUrl) {
            this.ignoreUrl = ignoreUrl;
        }
    }

}
spring:
  cloud:
    gateway:
      routes:
        - id: methodGet
          order: -100
          uri: http://localhost:8130
          predicates:
            - Method=GET
          filters:
            - name: PrintParam # 如果类名为  ???GatewayFilterFactory,则过滤器名为???
              args:
                ignoreUrl:
                  - /findAll
                  - /findOne

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沸腾的冰川

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值