Skip to content

3.spring cloud gateway

1.gateway的引入

​ 为什么使用网关?

2.gateway快速入门

2.1 专业术语 Glossary

  • Route: The basic building block of the gateway. It is defined by an ID, a destination URI, a collection of predicates, and a collection of filters. A route is matched if the aggregate predicate is true.

    路由 是gateway网关的基本组件,它是由一个 id,一个目标服务的地址,一组 断言,一组过滤器构成,如果断言匹配,则向目标服务的地址请求

  • Predicate: This is a Java 8 Function Predicate. The input type is a Spring Framework ServerWebExchange. This lets you match on anything from the HTTP request, such as headers or parameters.

    断言 这是 java8 的 函数式断言, 输入的是 spring框架中的 ServerWebExchange 组件,通过它可以得到代表请求的 request

  • Filter: These are instances of GatewayFilter that have been constructed with a specific factory. Here, you can modify requests and responses before or after sending the downstream request.

过滤器 这些实例都是GatewayFilter的实例,被一个特殊的工厂构建,他可以对请求和响应进行拦截处理。

2.2 工作流程

3.搭建网关服务

3.1 创建sb工程,添加依赖

shell
 <dependencies>
        <dependency>
            <groupId>com.qf.java2110</groupId>
            <artifactId>01-cloud-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>


        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.0</version>
        </dependency>
    </dependencies>

注意:不要添加spring-boot-starter-web 这个依赖,因为gateway 是基于 spring5的新特性,webflux,它不是基于servlet的,避免冲突。

3.2 配置文件

yml
spring:
  application:
    name: cloud-gateway     # 网关的服务名
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848      #注册中心的地址
        namespace: java2110
    gateway:
      routes[0]:    # 配置的第一个路由
        id: test_route1     # 路由id
        uri: http://www.baidu.com   # 目标  uri
        predicates[0]:      #   第一个断言
          Query=url,bd.     # The Query Route Predicate Factory

      routes[1]:
        id: customer_route
        uri: lb://cloud-customer
        predicates[0]:
          Path=/customer/**
        predicates[1]:
          After=2021-10-29T22:24:40.626+08:00[Asia/Shanghai]
        predicates[2]:
          MyHeader=name,zs     # 自定义断言  

      routes[2]:
        id: search_route
        uri: lb://cloud-search
        predicates[0]:
          Path=/search/**
        filters[0]:
          AddRequestHeader=Foo,Bar
        filters[1]:
          CalServiceTime=x,y     # 自定义过滤器



      routes[3]:
        id: search_route
        uri: lb://cloud-search
        predicates[0]:
          Path=/api/search/**
        filters[0]:
          RewritePath=/api(?<segment>/?.*), $\{segment}   # 实际开发当中 前端请求过来的路径 可能跟接口的路径不太匹配,所以可以 使用这个过滤器 进行  路径重写
server:
  port: 88

3.3 自定义断言

自定义谓词工厂的类名规范:后缀必须是RoutePredicateFactory

java
//自定义断言
package com.qf.gateway.predicates;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * @ClassName : MyHeaderRoutePredicateFactory
 * @Author : glls
 * @Date: 2021/12/22 10:56
 * @Description :
 */

@Component
public class MyHeaderRoutePredicateFactory extends AbstractRoutePredicateFactory<MyConfig> {

    public MyHeaderRoutePredicateFactory() {
        super(MyConfig.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(MyConfig config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange exchange) {

                String value = exchange.getRequest().getHeaders().getFirst(config.getKey());

                System.out.println(value);

                if(StringUtils.isEmpty(value)){
                    return false;
                }else{
                    if(value.equals(config.getValue())){
                        return true;
                    }else{
                        return false;
                    }
                }

            }
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        //- MyHeader=bbb,cccc
        //[bbb,cccc]
        //bbb赋值给MyConfig#key
        //cccc赋值给MyConfig#value

        return Arrays.asList("key","value");
    }
}

3.4 自定义过滤器

命名规范:过滤器工厂的类名必须以GatewayFilterFactory为后缀

java
//自定义过滤器
package com.qf.gateway.filters;

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.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;

/**
 * @ClassName : CalServiceTimeGatewayFilterFactory
 * @Author : glls
 * @Date: 2021/12/22 11:40
 * @Description :
 */
@Component
public class CalServiceTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<MyConfig> {

    public CalServiceTimeGatewayFilterFactory() {
        super(MyConfig.class);
    }

    @Override
    public GatewayFilter apply(MyConfig config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                //前处理
                long startTime = System.currentTimeMillis();

                System.out.println("name:"+config.getKey());
                System.out.println("value:"+config.getValue());

                // return chain.filter(exchange);//放⾏

                return chain.filter(exchange).then(
                        //后置处理
                        Mono.fromRunnable(()->{
                            System.out.println("post come in");
                            //获取系统当前时间戳为endTime
                            long entTime = System.currentTimeMillis();
                            System.out.println("接口响应时间time="+(entTime-startTime));

                        }));
            }
            };
        };


    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("key","value");
    }
}
java
package com.qf.gateway.filters;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @ClassName : MyConfig
 * @Author : glls
 * @Date: 2021/12/22 11:40
 * @Description :   建议 断言 和 过滤器 各建一个 这个类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyConfig {

    private String key;

    private String value;

}

4.全局过滤器的使用

java
package com.qf.gateway.filters;

import cn.hutool.json.JSONUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName : AuthFilter
 * @Author : glls
 * @Date: 2021/12/22 14:59
 * @Description :
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //过滤器的前处理
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        String token = request.getHeaders().getFirst("token");
        if(StringUtils.isEmpty(token)){
            Map res = new HashMap(){{
                put("msg", "没有登录!!");
            }};
            return response(response,res);
        }else{
            if(!"123".equals(token)){
                Map res = new HashMap(){{
                    put("msg", "令牌⽆效!!");
                }};
                return response(response,res);
            }else{
                return chain.filter(exchange); //放⾏
            }
        }
    }

    private Mono<Void> response(ServerHttpResponse response,Object
            msg){
        response.getHeaders().add("Content-Type",
                "application/json;charset=UTF-8");

        String resJson = JSONUtil.toJsonPrettyStr(msg);
        DataBuffer dataBuffer =
                response.bufferFactory().wrap(resJson.getBytes());
        return response.writeWith(Flux.just(dataBuffer));//响应json数据
    }



    @Override
    public int getOrder() {
        return 0;
    }
}