统⼀服务入口 - Gateway

网关介绍

问题

        在 spring cloud 体系中我们通过 Eureka,Nacos 解决了服务注册,服务发现的问题,使⽤Spring Cloud LoadBalance解决了负载均衡的问题,使⽤ OpenFeign 解决了远程调⽤的问题.

        但是当前所有微服务的接⼝都是直接对外暴露的,可以直接通过外部访问.为了保证对外服务的安全性,服务端实现的微服务接⼝通常都带有⼀定的权限校验机制.由于使⽤了微服务,原本⼀个应⽤的的多个模块拆分成了多个应⽤,我们不得不实现多次校验逻辑.当这套逻辑需要修改时,我们需要修改多个应⽤,加重了开发⼈员的负担.针对以上问题,常⽤的解决⽅案是使⽤API⽹关.

什么是 API 网关

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

        简单理解:我们在进行微服务开发时,每个微服务都提供了接口,但这些接口是用于进行业务处理的,不应该被暴露在外界,如果我们为每个微服务都设置权限管理,这是十分麻烦的。所以就需要  API 网关作为整个微服务架构的⻔⾯,所有的请求都要先交给 API 网关, API 网关校验后再通过服务注册中心将请求转发给对应的服务接口,由于 API 网关已经校验了,后续的微服务就不需要在校验请求了。

网关核⼼功能:

        权限控制:作为微服务的⼊⼝,对⽤户进⾏权限校验,如果校验失败则进⾏拦截

        动态路由:⼀切请求先经过⽹关,但⽹关不处理业务,⽽是根据某种规则,把请求转发到某个微服务

        负载均衡:当路由的⽬标服务有多个时,还需要做负载均衡,限流:请求流量过⾼时,按照⽹关中配置微服务能够接受的流量进⾏放⾏,避免服务压⼒过⼤.

Spring Cloud Gateway

快速上手

我们通过以下的演⽰,先来了解⽹关的基本功能

创建网关项目

        API ⽹关也是⼀个服务

引⼊⽹关依赖

        由于 API ⽹关在接收并检验用户请求后要将请求转发给对应的服务,所以  API ⽹关也需要注册到 nacos 上,所以引入 nacos 的依赖, API ⽹关应当均衡的向服务转发请求,需要用到负载均衡,所以引入负载均衡工具 loadbalancer 的依赖

<!--⽹关-->
<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>

编写启动类

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

添加 Gateway 的路由配置

创建 application.yml ⽂件,添加如下配置:

        凡是需要接收外界请求的服务都需要在网关这里配置路由,这样网关才能将请求发给对应的服务

server:
  port: 10030 # ⽹关端⼝
spring:
  application:
    name: gateway # 服务名称
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    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 参数指定的地址.

        当我们要为一个服务设置多个条件时,可以用 ,隔开

predicates:
  - Path=/order/**,/feign/**

        配置好以后我们就可以通过⽹关服务访问 product-service 和 order-service 了,10030 是网关的端口

        http://127.0.0.1:10030/product/1

        http://127.0.0.1:10030/order/1

Route Predicate Factories

Predicate

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

@FunctionalInterface
public interface Predicate<T> {
 boolean test(T t);
 //...
}

代码演⽰:

1. 定义⼀个 Predicate

class StringPredicate implements Predicate<String>{
 @Override
 public boolean test(String str) {
 return str.isEmpty();
 }
}

2. 使⽤这个 Predicate

public class PredictTest {
 public static void main(String[] args) {
 Predicate<String> predicate = new StringPredicate();
 System.out.println(predicate.test(""));
 System.out.println(predicate.test("bite666"));
 }
}

3. 运⾏结果

4. Predicate 的其他写法

1)内置函数

public class PredictTest {
 public static void main(String[] args) {
 Predicate<String> predicate = new Predicate<String>(){
 @Override
 public boolean test(String s) {
 return s.isEmpty();
 }
 };
 System.out.println(predicate.test(""));
 System.out.println(predicate.test("bite666"));
 }
}

2)lambda写法

public class PredictTest {
 public static void main(String[] args) {
 Predicate<String> predicate = s -> s.isEmpty();
 System.out.println(predicate.test(""));
 System.out.println(predicate.test("bite666"));
 }
}

5. Predicate 的其他⽅法

• isEqual(Object targetRef):⽐较两个对象是否相等,参数可以为Null

• and(Predicate other): 短路与操作,返回⼀个组成 Predicate

• or(Predicate other):短路或操作,返回⼀个组成 Predicate

• test(T t):传⼊⼀个 Predicate 参数,⽤来做判断

• negate():返回表⽰此 Predicate 逻辑否定的 Predicate

Route Predicate Factories(路由断言工厂)

        Route Predicate Factories(路由断⾔⼯⼚,也称为路由谓词⼯⼚,此处谓词表⽰⼀个函数),在Spring Cloud Gateway中, Predicate 提供了路由规则的匹配机制.我们在配置⽂件中写的断⾔规则只是字符串,这些字符串会被 Route Predicate Factory读取并处理,转变为路由判断的条件.

        ⽐如前⾯章节配置的 Path=/product/** ,就是通过 Path 属性来匹配 URL 前缀是 /product 的请求.这个规则是由 org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateF actory 来实现的. Spring Cloud Gateway 默认提供了很多 Route Predicate Factory,这些 Predicate会分别匹配 HTTP 请求的不同属性,并且多个 Predicate 可以通过 and 逻辑进⾏组合.

        

        

更多请参考:Route Predicate Factories :: Spring Cloud Gateway

代码演⽰:

在application.yml 中添加如下规则:

spring:
 cloud:
 gateway:
 routes: # ⽹关路由配置 
 - id: product-service #路由ID, ⾃定义, 唯⼀即可 
 uri: lb://product-service #⽬标服务地址 
 predicates: #路由条件 
 - Path=/product/**
 - After=2025-01-01T00:00:00.000+08:00[Asia/Shanghai]

增加限制路由规则:请求时间为 2025年1⽉1⽇之后

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

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

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

        Pre 类型过滤器:路由处理之前执⾏(请求转发到后端服务之前执⾏),在 Pre 类型过滤器中可以做鉴权, 限流等.

        Post 类型过滤器:请求执⾏完成后,将结果返回给客户端之前执⾏

        过滤器可有可⽆.

        Spring Cloud Gateway 中内置了很多Filter,⽤于拦截和链式处理 web 请求.⽐如权限校验,访问超时等设定. Spring Cloud Gateway 从作⽤范围上,把 Filter 可分为 GatewayFilterGlobalFilter.

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

GatewayFilter(过滤器)

        GatewayFilter 同 Predicate 类似,都是在配置⽂件 application.yml 中配置,每个过滤器的逻辑都是固定的.⽐如 AddRequestParameterGatewayFilterFactory 只需要在配置⽂件中写 AddRequestParameter ,就可以为所有的请求添加⼀个参数,我们先通过⼀个例⼦来演⽰ GatewayFilter 如何使⽤.

快速上手

1. 在 application.yml 中添加 filter

server:
    port: 10030 # ⽹关端⼝ 
spring:
    application:
        name: gateway # 服务名称 
    cloud:
        nacos:
            discovery:
                server-addr: 110.41.51.65:10020
        gateway:
            routes: # ⽹关路由配置 
              - id: product-service #路由ID, ⾃定义, 唯⼀即可 
                uri: lb://product-service #⽬标服务地址 
                predicates: #路由条件 
                  - Path=/product/**
                  - After=2024-01-01T00:00:00.000+08:00[Asia/Shanghai]
                filters:
                  - AddRequestParameter=userName, yulin
              - id: order-service
                uri: lb://order-service
                predicates:
                  - Path=/order/**

        该 filter 只添加在了 product-service 路由下,因此只对 product-service 路由⽣效, 也就是 对 /product/** 的请求⽣效.

        该 filter 表示,将请求发送给服务之前,向服务中添加一个 userName 参数,值为 yulin

filters:
  - AddRequestParameter=userName, yulin

        GatewayFilter说明: Spring Cloud Gateway提供了的 Filter ⾮常多,下⾯列出⼀些常⻅过滤器的说明.

详细可参考官⽅⽂档:GatewayFilter Factories :: Spring Cloud Gateway

Default Filters

        前⾯的 filter 添加在指定路由下,所以只对当前路由⽣效,若需要对全部路由⽣效,可以使⽤ spring.cloud.gateway.default-filters 这个属性需要⼀个 filter 的列表.

配置举例:

spring:
    cloud:
        gateway:
            default-filters:
            - AddResponseHeader=X-Response-Default-Red, Default-Blue
            - PrefixPath=/httpbin

GlobalFilter(全局过滤器)

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

 • Gateway Metrics Filter: ⽹关指标, 提供监控指标

• Forward Routing Filter: ⽤于本地 forword, 请求不转发到下游服务器

• LoadBalancer Client Filter: 针对下游服务,实现负载均衡.

• ...更多过滤器参考:Global Filters :: Spring Cloud Gateway

快速上手

1. 添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

2. 添加配置

spring:
    cloud:
        gateway:
            metrics:
                enabled: true
management:
    endpoints:
        web:
            exposure:
                include: "*"
    endpoint:
        health:
            show-details: always
        shutdown:
            enabled: true

3. 测试 http://127.0.0.1:10030/actuator,显⽰所有监控的信息链接

过滤器执⾏顺序

        ⼀个项⽬中,既有 GatewayFilter,⼜有 GlobalFilter 时,执⾏的先后顺序是什么呢?请求路由后,⽹关会把当前项⽬中的 GatewayFilter 和 GlobalFilter 合并到⼀个过滤器链(集合)中,并进⾏排序,依次执⾏过滤器.

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

        • Filter 通过实现 Order 接⼝或者添加 @Order 注解来指定 order 值.

        • Spring Cloud Gateway 提供的 Filter 由 Spring 指定.⽤户也可以⾃定义 Filter,由⽤户指定.

        • 当过滤器的 order 值⼀样时,会按照 defaultFilter > GatewayFilter > GlobalFilter 的顺序执⾏.

自定义过滤器

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

自定义 GatewayFilter

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

定义GatewayFilter

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
@Slf4j
@Service
public class CustomGatewayFilterFactory extends
        AbstractGatewayFilterFactory<CustomGatewayFilterFactory.CustomConfig>
        implements Ordered {
    public CustomGatewayFilterFactory() {
        super(CustomConfig.class);
    }
    @Override
    public GatewayFilter apply(CustomConfig config) {
        /**
         * Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain 
         chain)
         * ServerWebExchange: HTTP请求-响应交互的契约, 提供对HTTP请求和响应的访问, 服
         务器端请求属性, 请求实例,响应实例等, 类似Context⻆⾊ 
         * GatewayFilterChain: 过滤器链 
         * Mono: Reactor核⼼类, 数据流发布者, Mono最多只触发⼀个事件, 所以可以把 
         Mono ⽤于在异步任务完成时发出通知. 
         * Mono.fromRunnable: 创建⼀个包含Runnable元素的数据流 
         */
        return ((exchange, 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; //配置优先级, order越⼤, 优先级越低 
    }
}

针对这个 Filter 的配置,使⽤ CustomConfig 定义

@Data
public static class CustomConfig {
    private String name;
}

代码说明:

        1. 类名统⼀以 GatewayFilterFactory 结尾,因为默认情况下,过滤器的 name 会采⽤该定义类的前缀.这⾥的 name=Custom (yml配置中使⽤)

        2. apply⽅法中,同时包含 Pre 和 Post 过滤,then⽅法中是请求执⾏结束之后处理的

        3. CustomConfig 是⼀个配置类,该类只有⼀个属性 name,和 yml 的配置对应

        4. 该类需要交给 Spring 管理,所以需要加 @Service 注解

        5. getOrder表⽰该过滤器的优先级,值越⼤,优先级越低.

配置过滤器

spring:
    cloud:
        gateway:
            routes: # ⽹关路由配置 
                - id: product-service #路由ID, ⾃定义, 唯⼀即可 
                uri: lb://product-service #⽬标服务地址 
                predicates: #路由条件 
                    - Path=/product/**
                filters:
                    - name: Custom
                      args:
                        name: custom filter
⾃定义 GlobalFilter

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

定义GlobalFilter

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.Service;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Slf4j
@Service
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;//配置优先级, order越⼤, 优先级越低 
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小林想被监督学习

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

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

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

打赏作者

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

抵扣说明:

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

余额充值