SpringCloud-GateWay(五)

目录

一、了解GateWay

1、什么是服务网关

2、服务网关的基本功能 

 3、流量网关与服务网关的区别

主流网关的对比与选型

 二、GateWay初体验

 pom文件

启动类

配置文件

三、定制化全局过滤器GlobalFilter

CustomGlobalFilter

config配置(bean加载)

四、局部过滤器GatewayFilter

ElapsedTimeGatewayFilter

config配置

配置文件

五、自定义全局过滤器验证token 


一、了解GateWay

1、什么是服务网关

        传统的单体架构中只需要开放一个服务给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,如果没有网关,客户端只能在本地记录每个微服务的调用地址,当需要调用的微服务数量很多时,它需要了解每个服务的接口,这个工作量很大。那有了网关之后,能够起到怎样的改善呢?

网关作为系统的唯一流量入口,封装内部系统的架构,所有请求都先经过网关,由网关将请求路由到合适的微服务,所以,使用网关的好处在于:

(1)简化客户端的工作。网关将微服务封装起来后,客户端只需同网关交互,而不必调用各个不同服务;

(2)降低函数间的耦合度。 一旦服务接口修改,只需修改网关的路由策略,不必修改每个调用该函数的客户端,从而减少了程序间的耦合性

(3)解放开发人员把精力专注于业务逻辑的实现。由网关统一实现服务路由(灰度与ABTest)、负载均衡、访问控制、流控熔断降级等非业务相关功能,而不需要每个服务 API 实现时都去考虑

2、服务网关的基本功能 

 

 3、流量网关与服务网关的区别

流量网关(如Nignx)是指提供全局性的、与后端业务应用无关的策略,例如 HTTPS证书卸载、Web防火墙、全局流量监控等。

服务网关(如Spring Cloud Gateway)是指与业务紧耦合的、提供单个业务域级别的策略,如服务治理、身份认证等。也就是说,流量网关负责南北向流量调度及安全防护,微服务网关负责东西向流量调度及服务治理。

主流网关的对比与选型

(1)Kong 网关:Kong 的性能非常好,非常适合做流量网关,但是对于复杂系统不建议业务网关用 Kong,主要是工程性方面的考虑

(2)Zuul1.x 网关:Zuul 1.0 的落地经验丰富,但是性能差、基于同步阻塞IO,适合中小架构,不适合并发流量高的场景,因为容易产生线程耗尽,导致请求被拒绝的情况

(3)gateway 网关:功能强大丰富,性能好,官方基准测试 RPS (每秒请求数)是Zuul的1.6倍,能与 SpringCloud 生态很好兼容,单从流式编程+支持异步上也足以让开发者选择它了。

(4)Zuul 2.x:性能与 gateway 差不多,基于非阻塞的,支持长连接,但 SpringCloud 没有集成 zuul2 的计划,并且 Netflix 相关组件都宣布进入维护期,前景未知。

综上,gateway 网关更加适合 SpringCloud 项目,而从发展趋势上看,gateway 替代 zuul 也是必然的。

 二、GateWay初体验

 

 pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.chensir</groupId>
        <artifactId>spring-cloud-root</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <artifactId>spring-cloud-gateway</artifactId>

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

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>

        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

启动类

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

}

配置文件


server:
  port: 8083
spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        - id: rout01
          uri: http://httpbin.org:80/
          predicates:   # 断言
            - Path=/a/**  # 断言请求的url二级是否以a开头,若是以a开头就转发到上方uri(http://httpbin.org:80/)路径 比如:请求前端请求为http://localhost:8080/a/login,由于二级路径为a所以会转发到http://httpbin.org:80/a/login
          filters:
            - StripPrefix=1 # 转发后是否去除path前缀,默认为0不去除前缀 1去除前缀. 比如此示例前缀为Path=/a/** 若是去除前缀转发后就为http://httpbin.org:80/login
            - AddRequestParameter=red, blue # 在请求参数上会加上 color = blue   key为color,value为blue
            - AddResponseHeader=name,zhangbozhi # 响应头添加信息
            - AddRequestHeader=sign,123 # 请求头添加信息

        - id: rout02
          uri: https://blog.csdn.net/weixin_45326523
          predicates:
            - Path=/b/**
          filters:
            - StripPrefix=1
      # 全局
      default-filters:
        - AddRequestHeader=sign,123 # 添加请求头
        - AddResponseHeader=company,206
        - AddRequestHeader=traceid,5nifaiea8787808877

# 此配置文件并不实用(比如想在每个接口都加个traceid并且值不唯一,再使用此配置文件就达不到需求了),通常我们都是去定制化局部过滤器与全局过滤器 可见 三和四




在配置文件中路由route主要由id、目标uri、断言和过滤器集合组成。

(1)id:路由标识,要求唯一,名称任意(默认值uuid,一般不用,需要自己定义)

(2)uri:请求最终被转发到的目标地址

(3)order:路由优先级,数字越小,优先级越高

(4)predicates:断言数组(支持多个断言,如有多个需要都符合断言条件),即判断条件;如果返回值是true,则转发请求到uri属性指定的服务中。

(5)filters:过滤器数组,在请求传递过程中,对请求做一些修改

在此示例中如果请求url为http://localhost:8083/a/... 就会转发到 http://httpbin.org:80/上并且不携带二级目录a(因为StripPrefix=1),如果请求url为http://localhost:8083/b/... 就会转发到 https://blog.csdn.net/weixin_45326523 上并且不携带二级目录b。

三、定制化全局过滤器GlobalFilter

 

配置文件中把全局的过滤去掉,pom文件不变;

CustomGlobalFilter

//全局过滤器
public class CustomGlobalFilter implements GlobalFilter, Ordered {

    @Override     //exchange 交换机
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //通过交换机获取的request只能读 不能写
        ServerHttpRequest request = exchange.getRequest();

        //需要mutate下 就可以操作request
        ServerHttpRequest request2 = request.mutate().header("traceid", IdUtil.fastUUID()).build();

        // exchange.mutate().request(request2).build() 把新的放进去,chain.filter链式调用
        return chain.filter(exchange.mutate().request(request2).build());
    }

    @Override
    public int getOrder() {
        //路由优先级,数字越小,优先级越高
        return -1;
    }
}

config配置(bean加载)

@Configuration
public class FilterConfig {

    @Bean
    public GlobalFilter customFilter() {
        return new CustomGlobalFilter();
    }

}

这样就可以对gateway网关进行全局定制化!

四、局部过滤器GatewayFilter

 

ElapsedTimeGatewayFilter


//记录接口耗时
public class ElapsedTimeGatewayFilter implements GatewayFilter, Ordered {
    private final static String BEAGIN = "begin";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        exchange.getAttributes().put(BEAGIN, System.currentTimeMillis());

        return chain.filter(exchange).then(
                Mono.fromRunnable(() -> {
                    long startTime = exchange.getAttribute(BEAGIN);
                    long endTime = System.currentTimeMillis();
                    String url = exchange.getRequest().getURI().getRawPath();
                    System.out.println(StrUtil.format("{}耗时:{}", url, endTime - startTime));

                })

        );
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

该过滤器实现的GatewayFilter接口,而全局过滤器实现的是GlobalFilter接口。

而实现的接口结构都一样 不同的是bean的加载方式不同 等。

config配置

@Component
public class ElapsedTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
    @Override
    public GatewayFilter apply(Object config) {
        return new ElapsedTimeGatewayFilter();
    }
}

至此 局部过滤器并不能生效,因为还没指定哪个路由标识(id)去使用他。因此需要在配置文件中加上  ElapsedTime;

配置文件


server:
  port: 8083
spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        - id: rout01
          uri: http://localhost:9090/
          predicates:   # 断言
            - Path=/a/**  # 断言请求的url二级是否以a开头,若是以a开头就转发到上方uri(http://httpbin.org:80/)路径 比如:请求前端请求为http://localhost:8080/a/login,由于二级路径为a所以会转发到http://httpbin.org:80/a/login
          filters:
            - StripPrefix=1 # 转发后是否去除path前缀,默认为0不去除前缀 1去除前缀. 比如此示例前缀为Path=/a/** 若是去除前缀转发后就为http://httpbin.org:80/login
            - AddRequestParameter=red, blue # 在请求参数上会加上 color = blue   key为color,value为blue
            - AddResponseHeader=name,zhangbozhi # 响应头添加信息
            - AddRequestHeader=sign,123 # 请求头添加信息
            - ElapsedTime  # 配合局部过滤器使用
        - id: rout02
          uri: https://blog.csdn.net/weixin_45326523
          predicates:
            - Path=/b/**
          filters:
            - StripPrefix=1
     



加上了 -ElapsedTime 这样在使用rout01标识时才会走局部过滤器逻辑!

五、自定义全局过滤器验证token 

@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {

    private static final String  TOKEN_SECRET ="chensir123";
    @Bean
    public GlobalFilter customFilter() {
        return new CustomGlobalFilter();
    }
    
    @Override
    public Mono<Void> CustomGlobalFilter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        List<String> tokenList = request.getHeaders().get("Authorization");
        //校验是否为空
        if (ObjectUtil.isEmpty(tokenList)) {

            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return    response.setComplete();
        }
        String  token = tokenList.get(0);
        //校验是否正确(合法)
        boolean isTrue = false;
        try {
            isTrue = JWTUtil.verify(token, TOKEN_SECRET.getBytes());
        } catch (Exception ex){
            ex.printStackTrace();
        }
        if (isTrue == false) {
            //设置http响应状态码
            response.setStatusCode(HttpStatus.UNAUTHORIZED);//401 未认证
           return response.setComplete();
        }

        //走到这个位置,说明token是正确合法的
        final JWT jwt = JWTUtil.parseToken(token);

        System.out.println(jwt.getPayload().toString());

        UserInfo userInfo = JSONUtil.toBean(jwt.getPayload().toString(), UserInfo.class);
        Integer userId = userInfo.getUserId();

        //复制一份原来的request,request2
        ServerHttpRequest request2 = request.mutate().header("userId", String.valueOf(userId)).build();

        //证明token验证成功,可以正常调用后方的具体接口了
       // chain.filter(exchange)
        //复制一份交换机,把我们上一步新建的request2 放进去,之后继续执行.
        return chain.filter(exchange.mutate().request(request2).build());

    }

    @Override
    public int getOrder() {
        return -1;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值