【JavaEE】【SpringCloud】网关_GateWay

目录

  • 一、网关
    • [1.1 API网关是什么](#1.1 API网关是什么)
    • [1.2 常见网关实现](#1.2 常见网关实现)
  • 二、使用GateWay
  • [三、Route Predicate Factories](#三、Route Predicate Factories)
    • [3.1 Predicate](#3.1 Predicate)
    • [3.2 Route Predicate Factories](#3.2 Route Predicate Factories)
    • [3.3 Gateway Filter Factories(⽹关过滤器⼯⼚)](#3.3 Gateway Filter Factories(⽹关过滤器⼯⼚))
      • [3.3.1 GatewayFilter](#3.3.1 GatewayFilter)
      • [3.3.2 GlobalFilter](#3.3.2 GlobalFilter)
    • [3.4 过滤器执⾏顺序](#3.4 过滤器执⾏顺序)
    • [3.5 ⾃定义过滤器](#3.5 ⾃定义过滤器)
      • [3.5.1 自定义GatewayFilter](#3.5.1 自定义GatewayFilter)
      • [3.5.2 自定义GlobalFilter](#3.5.2 自定义GlobalFilter)

一、网关

我们在使用微服务的时候,所有接口都是直接暴露的,如果像原来单机架构一样,使用过滤器就需要每一个服务都去写,非常麻烦。针对这个问题,⼀个常⽤的解决⽅案是使⽤API⽹关。

1.1 API网关是什么

API⽹关(简称⽹关)也是⼀个服务,通常是后端服务的唯⼀⼊⼝。它的定义类似设计模式中的Facade模式(⻔⾯模式,也称外观模式)。它就类似整个微服务架构的⻔⾯,所有的外部客⼾端访问,都需要经过它来进⾏调度和过滤。相当于大门。

核心功能:

  • 权限控制:作为微服务的⼊⼝,对⽤⼾进⾏权限校验,如果校验失败则进⾏拦截
  • 动态路由:⼀切请求先经过⽹关,但⽹关不处理业务,⽽是根据某种规则,把请求转发到某个微服务
  • 负载均衡:当路由的⽬标服务有多个时,还需要做负载均衡
  • 限流:请求流量过⾼时,按照⽹关中配置微服务能够接受的流量进⾏放⾏,避免服务压⼒过⼤。

1.2 常见网关实现

业界常⽤的⽹关⽅式有很多,技术⽅案也较成熟,其中不乏很多开源产品,⽐如Nginx,Kong,Zuul,Spring Cloud Gateway等。下⾯介绍两种常⻅的⽹关⽅案。

Zuul

Zuul 是 Netflix 公司开源的⼀个API⽹关组件,是Spring Cloud Netflix ⼦项⽬的核⼼组件之⼀,它可以和 Eureka、Ribbon、Hystrix 等组件配合使⽤。

在Spring Cloud Finchley正式版之前,Spring Cloud推荐的⽹关是Netflix提供的Zuul(此处指Zuul 1.X)。

然⽽Netflix在2018年宣布⼀部分组件进⼊维护状态,不再进⾏新特性的开发。这部分组件中就包含Zuul。

Spring Cloud Gateway

Spring Cloud Gateway 是Spring Cloud的⼀个全新的API⽹关项⽬,基于Spring + SpringBoot等技术开发,⽬的是为了替换掉Zuul。旨在为微服务架构提供⼀种简单⽽有效的途径来转发请求,并为他们提供横切关注点,⽐如:安全性,监控/指标和弹性。

在性能⽅⾯,根据官⽅提供的测试报告,Spring Cloud Gateway的RPS(每秒请求数)是Zuul的1.6倍。
https://github.com/spencergibb/spring-cloud-gateway-bench

二、使用GateWay

基于前面的feign的代码开发。

  1. 创建网关项目

  2. 引入依赖

xml 复制代码
    <dependencies>
        <!--⽹关-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--基于nacos实现服务发现依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--负载均衡-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

    </dependencies>
  1. 编写启动类
java 复制代码
package com.cloud.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}
  1. 配置文件
java 复制代码
server:
  port: 10030 # ⽹关端⼝
spring:
  application:
    name: gateway # 服务名称
  cloud:
    nacos:
      discovery:
        server-addr: nacos的ip地址+端口号
    gateway:
      routes: # ⽹关路由配置
        - id: product-service #路由ID, ⾃定义, 唯⼀即可
          uri: lb://product-service #⽬标服务地址
          predicates: #路由条件
            - Path=/product/**
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**

配置字段说明:

  • id:⾃定义路由ID,保持唯⼀
  • uri:⽬标服务地址,⽀持普通URI 及 lb://应⽤注册服务名称 。lb表⽰负载均衡,使⽤ lb:// ⽅式表⽰从注册中⼼获取服务地址。
  • predicates:路由条件,根据匹配结果决定是否执⾏该请求路由,上述代码中,我们把符合Path规则的⼀切请求,都代理到uri参数指定的地址。


三、Route Predicate Factories

3.1 Predicate

Predicate是Java 8提供的⼀个函数式编程接⼝,它接收⼀个参数并返回⼀个布尔值,⽤于条件过滤,请求参数的校验。

java 复制代码
@FunctionalInterface
public interface Predicate<T> {
 boolean test(T t);
 //...
}

写一个判断字符串为空的Predicate函数:

java 复制代码
package predicate;

import java.util.function.Predicate;
/**
 * 字符串为空或null时返回true
 */
public class StringPredicate implements Predicate<String> {
    @Override
    public boolean test(String s) {
        return s == null || s.isEmpty();
    }
}

使用匿名内部类和lambda表达式实现:

java 复制代码
package predicate;

import org.junit.Test;

import java.util.function.Predicate;

public class StringPredicateTest {
    @Test
    public void test() {
        StringPredicate stringPredicate = new StringPredicate();
        System.out.println(stringPredicate.test("")); // true
        System.out.println(stringPredicate.test("123")); // false
        System.out.println(stringPredicate.test(null)); // true

    }
    //匿名内部类
    @Test
    public void test2() {
        Predicate<String> stringPredicate = new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s == null || s.isEmpty();
            }
        };
        System.out.println(stringPredicate.test("")); // true
        System.out.println(stringPredicate.test("123")); // false
        System.out.println(stringPredicate.test(null)); // true
    }
    //lambda表达式
    @Test
    public void test3() {
        Predicate<String> stringPredicate = s -> s == null || s.isEmpty();
        System.out.println(stringPredicate.test("")); // true
        System.out.println(stringPredicate.test("123")); // false
        System.out.println(stringPredicate.test(null)); // true
    }
}

其他方法:

  • isEqual(Object targetRef) :⽐较两个对象是否相等,参数可以为Null
  • and(Predicate other):短路与操作,返回⼀个组成Predicate
  • or(Predicate other):短路或操作,返回⼀个组成Predicate
  • test(T t):传⼊⼀个Predicate参数,⽤来做判断
  • negate():返回表⽰此Predicate逻辑否定的Predicate
⽅法 说明
boolean test(T t) 判断条件,可以理解为 条件A根据逻辑返回布尔值
Predicate and(Predicate<? super T> other) 条件A && 条件B 当前Predicate的test⽅法 && other的test⽅法,相当于进⾏两次判断
default Predicate negate() ! 条件A
default Predicate or(Predicate<? super T> other) 条件A

3.2 Route Predicate Factories

Route Predicate Factories (路由断⾔⼯⼚,也称为路由谓词⼯⼚,此处谓词表⽰⼀个函数),在Spring Cloud Gateway中,Predicate提供了路由规则的匹配机制。

我们在配置⽂件中写的断⾔规则只是字符串,这些字符串会被Route Predicate Factory读取并处理,转变为路由判断的条件。

这个规则是由org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory 来实现的。

Spring Cloud Gateway 默认提供了很多Route Predicate Factory,这些Predicate会分别匹配HTTP请求的不同属性,并且多个Predicate可以通过and逻辑进⾏组合。

名称 说明 ⽰例
After 这个⼯⼚需要⼀个⽇期时间(Java的 ZonedDateTime对象),匹配指定⽇期之后的请求 predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver]
Before 匹配指定⽇期之前的请求 predicates: - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
Between 匹配两个指定时间之间的请求 datetime2 的参数必须在 datetime1 之后 predicates: - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
Cookie 请求中包含指定Cookie,且该Cookie值符合指定的正则表达式 predicates: - Cookie=chocolate, ch.p
Header 请求中包含指定Header,且该 Header值符合指定的正则表达式 predicates: - Header=X-Request-Id, \d+
Host 请求必须是访问某个host(根据请求中的Host字段进⾏匹配) predicates: -Host=**.somehost.org,**.anotherhost.org
Method 匹配指定的请求⽅式 predicates: - Method=GET,POST
Path 匹配指定规则的路径 predicates: - Path=/red/{segment},/blue/{segment}
Remote Addr 请求者的IP必须为指定范围 predicates: - RemoteAddr=192.168.1.1/24

参考链接:https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway-server-webflux/request-predicates-factories.html#page-title

3.3 Gateway Filter Factories(⽹关过滤器⼯⼚)

Predicate决定了请求由哪⼀个路由处理,如果在请求处理前后需要加⼀些逻辑,这就是Filter(过滤器)的作⽤范围了。

Filter分为两种类型:Pre类型和Post类型。

  • Pre类型过滤器:路由处理之前执⾏(请求转发到后端服务之前执⾏),在Pre 类型过滤器中可以做鉴权,限流等。
  • Post类型过滤器:请求执⾏完成后,将结果返回给客⼾端之前执⾏。

Spring Cloud Gateway 中内置了很多Filter,⽤于拦截和链式处理web请求。⽐如权限校验,访问超时等设定。

Spring Cloud Gateway从作⽤范围上,把Filter可分为GatewayFilter 和GlobalFilter。

  • GatewayFilter:应⽤到单个路由或者⼀个分组的路由上。
  • GlobalFilter:应⽤到所有的路由上,也就是对所有的请求⽣效。

3.3.1 GatewayFilter

官方文档:https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway-server-webflux/gatewayfilter-factories.html#page-title

GatewayFilter 同 Predicate 类似,都是在配置⽂件 application.yml 中配置,每个过滤器的逻辑都是固定的。⽐如 AddRequestParameterGatewayFilterFactory 只需要在配置⽂件中写 AddRequestParameter ,就可以为所有的请求添加⼀个参数。

名称 说明 示例
AddRequestHeader 为当前请求添加 Header 参数:Header 的名称及值 -AddRequestHeader=X-Request-red,blue
AddRequestParameter 为当前请求添加请求参数 参数:参数的名称及值 -AddRequestParameter=red,blue
AddResponseHeader 为响应结果添加 Header 参数:Header 的名称及值 -AddResponseHeader=X-Response-Red,Blue
RemoveRequestHeader 从当前请求删除某个 Header 参数:Header 的名称 -RemoveRequestHeader=X-Request-Foo
RemoveResponseHeader 从响应结果删除某个 Header 参数:Header 的名称 -RemoveResponseHeader=X-Response-Foo
RequestRateLimiter 为当前网关的所有请求执行限流过滤,若被限流默认返回 HTTP 429 Too Many Requests。 默认提供 RedisRateLimiter 的限流实现,采用令牌桶算法: - redis-rate-limiter.replenishRate:令牌填充速度,即每秒钟允许多少个请求(不丢弃任何请求) - redis-rate-limiter.burstCapacity:令牌桶容量,即每秒用户最大能够执行的请求数量(不丢弃任何请求),设为 0 将阻止所有请求 - redis-rate-limiter.requestedTokens:每次请求占用几个令牌,默认为 1 filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 10 redis-rate-limiter.burstCapacity: 20 redis-rate-limiter.requestedTokens: 1
Retry 针对不同响应进行重试。当后端服务不可用时,网关根据配置参数发起重试请求。 - retries:重试次数,默认为 3 - statuses:HTTP 请求返回的状态码,针对指定状态码进行重试,对应 org.springframework.http.HttpStatus filters: - name: Retry args: retries: 3 statuses: BAD_REQUEST
RequestSize 设置允许接收的最大请求包大小,若请求包大小超过设定值,则返回 413 Payload Too Large。 请求包大小单位为字节,默认值为 5 MB。 filters: - name: RequestSize args: maxSize: 5000000
默认过滤器 添加一个 filter 并将其应用于所有路由,该属性需要一个 filter 列表,对所有路由都生效。 spring: cloud: gateway: default-filters:

3.3.2 GlobalFilter

官方链接:https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway-server-webflux/global-filters.html#gateway-combined-global-filter-and-gatewayfilter-ordering

GlobalFilter是Spring Cloud Gateway中的全局过滤器,它和GatewayFilter的作⽤是相同的。GlobalFilter 会应⽤到所有的路由请求上,全局过滤器通常⽤于实现与安全性,性能监控和⽇志记录等相关的全局功能。

Spring Cloud Gateway 内置的全局过滤器也有很多,⽐如:

  • Gateway Metrics Filter:⽹关指标,提供监控指标
  • Forward Routing Filter:⽤于本地forword,请求不转发到下游服务器
  • LoadBalancer Client Filter:针对下游服务,实现负载均衡。

3.4 过滤器执⾏顺序

⼀个项⽬中,既有GatewayFilter,⼜有 GlobalFilter时,执⾏的先后顺序是:

请求路由后,⽹关会把当前项⽬中的GatewayFilter和GlobalFilter合并到⼀个过滤器链(集合)中,并进⾏排序,依次执⾏过滤器。

每⼀个过滤器都必须指定⼀个int类型的order值,默认值为0,表⽰该过滤的优先级。order值越⼩,优先级越⾼,执⾏顺序越靠前。

  • Filter通过实现Order接⼝或者添加@Order注解来指定order值。
  • Spring Cloud Gateway提供的Filter由Spring指定。⽤⼾也可以⾃定义Filter,由⽤⼾指定。
  • 当过滤器的order值⼀样时,会按照 defaultFilter > GatewayFilter > GlobalFilter的顺序执⾏。

3.5 ⾃定义过滤器

Spring Cloud Gateway提供了过滤器的扩展功能,开发者可以根据实际业务来⾃定义过滤器,同样⾃定义过滤器也⽀持GatewayFilter 和 GlobalFilter两种。

3.5.1 自定义GatewayFilter

⾃定义GatewayFilter,需要去实现对应的接⼝ GatewayFilterFactory ,Spring Boot 默认帮我们实现的抽象类是 AbstractGatewayFilterFactory ,我们可以直接使⽤。

我们在前面配置文件中使用过滤器的时候会传参数,我们需要自定义类来接收参数。

java 复制代码
package com.cloud.gateway;

import lombok.Data;

@Data
public class CustomConfig {
    private String name;
}

自定义过滤器:

  1. 类名统⼀以GatewayFilterFactory结尾,因为默认情况下,过滤器的name会采⽤该定义类的前缀。这⾥的name=Custom(yml配置中使⽤)
  2. apply⽅法中,同时包含Pre和Post过滤,then⽅法中是请求执⾏结束之后处理的
  3. CustomConfig 是⼀个配置类,该类只有⼀个属性name,和yml的配置对应
  4. 该类需要交给Spring管理,所以需要加 @Component 注解
  5. getOrder表⽰该过滤器的优先级,值越⼤,优先级越低。
java 复制代码
package com.cloud.gateway;

import lombok.extern.slf4j.Slf4j;
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.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
@Slf4j
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomConfig> implements Ordered {
    public CustomGatewayFilterFactory() {
        super(CustomConfig.class);
    }

    /**
            * Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
            * ServerWebExchange: HTTP请求-响应交互的契约, 提供对HTTP请求和响应的访问, 服务器端请求属性, 请求实例,响应实例等, 类似Context⻆⾊
            * GatewayFilterChain: 过滤器链
            * Mono: Reactor核⼼类, 数据流发布者, Mono最多只触发⼀个事件, 所以可以把Mono ⽤于在异步任务完成时发出通知.
            * Mono.fromRunnable: 创建⼀个包含Runnable元素的数据流
    */

    @Override
    public GatewayFilter apply(CustomConfig config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                log.info("[Pre] Filter Request, name:"+config.getName());
                return chain.filter(exchange).then(Mono.fromRunnable(()->{
                    log.info("[Post] Response Filter");
                }));

            }
        };
    }

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

配置:

yml 复制代码
server:
  port: 10030 # ⽹关端⼝
spring:
  application:
    name: gateway # 服务名称
  cloud:
    gateway:
      routes: # ⽹关路由配置
        - id: product-service #路由ID, ⾃定义, 唯⼀即可
          uri: lb://product-service #⽬标服务地址
          predicates: #路由条件
            - Path=/product/**
          filters:
            - name: Custom
              args:
                name: custom filter

3.5.2 自定义GlobalFilter

GlobalFilter的实现⽐较简单,它不需要额外的配置,只需要实现GlobalFilter接⼝,⾃动会过滤所有的Filter。

java 复制代码
package com.cloud.gateway;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            log.info("[Pre] CustomGlobalFilter enter...");
            return chain.filter(exchange).then(Mono.fromRunnable(()->{
                log.info("[Post] CustomGlobalFilter return...");
            }));

        }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}
相关推荐
蓝眸少年CY3 小时前
(第十三篇)spring cloud之Sleuth分布式链路跟踪
分布式·spring·spring cloud
wfsm3 小时前
【无标题】
spring cloud
短剑重铸之日3 小时前
《SpringCloud实用版》 Gateway 4.3.x 保姆级实战:路由 + 限流 + 鉴权 + 日志全覆盖
java·后端·spring cloud·架构·gateway
ZealSinger3 小时前
Nacos2.x 内存注册表:从服务调用链路深入理解
java·spring boot·spring·spring cloud·nacos
编程彩机3 小时前
互联网大厂Java面试:从微服务到分布式事务的技术深度解析
java·spring cloud·微服务·分布式事务·saga·电商平台
Roye_ack4 小时前
【微服务 Day6】SpringCloud实战开发(RabbitMQ高级篇 + 死信交换机、延迟消息)
spring cloud·微服务·rabbitmq·mq
马尔代夫哈哈哈4 小时前
Spring Mvc(二)
java·spring boot·spring·servlet·java-ee
我叫果冻5 小时前
告别 “理论选手”:用 g-note 打通 Java 中高级技术的任督二脉
spring boot·spring cloud
鸽鸽程序猿5 小时前
【JavaEE】【SpringCloud】远程调用_OpenFeign
java·spring cloud·java-ee