统⼀服务⼊⼝-Gateway

embedded/2024/12/22 17:43:48/

1. 网关介绍

1.1 问题

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

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

针对以上问题, ⼀个常⽤的解决⽅案是使⽤API⽹关.

举个例子,当一个要去某个公司时,前台会进行身份校验,如果没有前台,每个部门就要单独校验,效率低,流程多,前台就类似于API网关

1.2 什么是API网关

API⽹关(简称⽹关)也是⼀个服务, 通常是后端服务的唯⼀⼊⼝.

它的定义类似设计模式中的Facade模式 (⻔⾯模式, 也称外观模式). 它就类似整个微服务架构的⻔⾯, 所有的外部客⼾端访问, 都需要经过它来进 ⾏调度和过滤. 

网关核心功能:

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

1.3 常见网关实现

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. 旨在为微服务架构提供⼀种简单⽽有效的途径来转发请求, 并为他们提供横切关注点, ⽐如: 安全性, 监控/指标和弹性.

2. Spring Cloud Gateway

2.1 使用

2.1.1 创建网关项目

2.1.2 依赖

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

2.1.3 启动类

 

2.1.4 添加Gateway的路由配置

server:port: 10030 #网关端口
spring:application:name: gateway #服务名称cloud:nacos:discovery:server-addr: 127.0.0.1:10020gateway:routes:- id: order-service #路由规则id,随便起,不重复uri: lb://order-service/  #路由到哪里,目标服务地址predicates:- Path=/order/**,/feign/**  #条件- After=2016-01-20T17:42:47.789-07:00[America/Denver]- id: product-serviceuri: lb://product-service/predicates:- Path=/product/**

uri:

⽬标服务地址, ⽀持普通URI 及 lb://应⽤注册服务名称 . lb表⽰负载均衡, 使⽤ lb:// ⽅ 式表⽰从注册中⼼获取服务地址. 

predicates:

路由条件, 根据匹配结果决定是否执⾏该请求路由, 上述代码中, 我们把符合Path规则的⼀切请求, 都代理到uri参数指定的地址.

2.1.5 测试

1. 通过⽹关服务访问product-service

http://127.0.0.1:10030/product/1001

url符合yml文件中配置的/product/**规则,路由转发到http://product-service/product/1001

2. 通过⽹关服务访问order-service

http://127.0.0.1:10030/order/1

2.2 Route Predicate Factories

2.2.1 Predicate

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

①定义一个Predicate

java">/*** 判定字符串是否为空-true*/
public class StrPredicate implements Predicate<String> {@Overridepublic boolean test(String s) {return s == null || s.isEmpty();}
}

②测试

java">public class PredicateTest {@Testpublic void test(){Predicate<String> predicate = new StrPredicate();System.out.println(predicate.test(""));System.out.println(predicate.test("aa"));}
}

③其它用法

1)内置函数

java">/*** 匿名内部类*/@Testpublic void test2(){Predicate<String> predicate = new Predicate<String>(){@Overridepublic boolean test(String s) {return s==null||s.isEmpty();}};System.out.println(predicate.test(""));System.out.println(predicate.test("aa"));}

2)lambda写法

java"> /*** lambda表达式*/@Testpublic void test3(){Predicate<String> predicate = s -> s==null||s.isEmpty(); //Predicate<String> isEmpty = String::isEmpty;System.out.println(predicate.test(""));System.out.println(predicate.test("aa"));}

④其它方法

  • isEqual(Object targetRef) :⽐较两个对象是否相等,参数可以为Null
  • and(Predicate other): 短路与操作,返回⼀个组成Predicate
  • or(Predicate other) :短路或操作,返回⼀个组成Predicate
  • test(T t) :传⼊⼀个Predicate参数,⽤来做判断
  • negate() : 返回表⽰此Predicate逻辑否定的Predicate
java">/*** 非*/@Testpublic void test4(){Predicate<String> predicate = s -> s==null||s.isEmpty();System.out.println(predicate.negate().test(""));System.out.println(predicate.negate().test("aa"));}/*** 或* 判定字符串为aa/bb*/@Testpublic void test5(){Predicate<String> predicate = s -> "aa".equals(s);Predicate<String> predicate2 = s -> "bb".equals(s);System.out.println(predicate.or(predicate2).test(""));System.out.println(predicate.or(predicate2).test("aa"));}/*** and* 判定字符串不为空且由数字组成,"12" "34"*/@Testpublic void test6(){Predicate<String> predicate = s -> s!=null && !s.isEmpty();Predicate<String> predicate2 = s -> s!=null && s.chars().allMatch(Character::isDigit);System.out.println(predicate.and(predicate2).test("aa"));System.out.println(predicate.and(predicate2).test("123"));}@Testpublic void test7(){System.out.println(ZonedDateTime.now());}

2.2.2 Route Predicate Factories

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

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

⽐如前⾯配置的 Path=/product/** , 就是通过Path属性来匹配URL前缀是 /product 的请求.

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

比如:

After,这个⼯⼚需要⼀个⽇期时间(Java 的 ZonedDateTime对象), 匹配指定⽇期之后的请求

参考:

Route Predicate Factories :: Spring Cloud Gateway

2.2.3 演示

①添加Predicate规则

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

测试:

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

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

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

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

举个例子:

去景区玩要先安检验票,如果当天人流量过多,就会限流---Pre过滤器

游玩之后,评价啊--Post过滤器

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

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

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

2.3.1 GatewayFilter

GatewayFilter 同 Predicate 类似, 都是在配置⽂件 application.yml 中配置,每个过滤器的逻辑都是固定的.

⽐如 AddRequestParameterGatewayFilterFactory 只需要在配置⽂件中写 AddRequestParameter , 就可以为所有的请求添加⼀个参数.

演示

①在application.yml中添加filter

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

②接收参数并打印

在 product-service 服务中接收请求的参数,并打印出来

说明:

Spring Cloud Gateway提供的Filter⾮常多

详细可参考官⽅⽂档 GatewayFilter Factories

举个例子:

Retry=>针对不同的响应进⾏重试. 当后端服务不可⽤ 时, ⽹关会根据配置参数来发起重试请求.

retries:

重试次数, 默认为3

status:

HTTP请求返回的状态码, 针对指定状态码进⾏重试. 对应 org.springframework.http.HttpStatus

 

Default Filters

前⾯的filter添加在指定路由下, 所以只对当前路由⽣效, 若需要对全部路由⽣效, 可以使⽤Default Filters

2.3.2 GlobalFilter

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

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

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

更多过滤器参考: Global Filters

使用

①依赖

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

②配置

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

③测试 

http://127.0.0.1:10030/actuator,显示所有监控的信息链接

 

2.4 过滤器执⾏顺序

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

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

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

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

2.5 自定义过滤器

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

2.5.1 ⾃定义GatewayFilter

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

Ⅰ.定义

java">@Slf4j
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomConfig> implements Ordered {public CustomGatewayFilterFactory() {super(CustomConfig.class);}@Overridepublic GatewayFilter apply(CustomConfig config) {return new GatewayFilter() {/*** ServerWebExchange:HTTP请求和交互契约,提供了对HTTP请求和相应的访问* GatewayFilterChain:过滤器链* Mono:Reactor核心类,数据流发布者,最多只能触发一个事件* chain.filter(exchange):执行请求* Mono.fromRunnable():创建一个包含Runnable元素的数据流*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//Pre类型  执行请求 Post类型log.info("Pre Filter,config:{}",config);  //pre类型代码逻辑return chain.filter(exchange).then(Mono.fromRunnable(()->{log.info("Post Filter..."); //Post}));}};}@Overridepublic int getOrder() {return Ordered.LOWEST_PRECEDENCE;}
}

Ⅱ.针对这个Filter的配置,使用CustomConfig定义

java">@Data
public class CustomConfig {private String name;
}
  • 类名统⼀以GatewayFilterFactory结尾, 因为默认情况下, 过滤器的name会采⽤该定义类的前缀. 这⾥的name=Custom(yml配置中使⽤)
  • apply⽅法中, 同时包含Pre和Post过滤, then⽅法中是请求执⾏结束之后处理的
  • CustomConfig 是⼀个配置类, 该类只有⼀个属性name, 和yml的配置对应
  • 该类需要交给Spring管理, 所以需要加 @Service 注解
  • getOrder表⽰该过滤器的优先级, 值越⼤, 优先级越低.

Ⅲ.配置过滤器

Ⅳ.测试

 

2.5.2 ⾃定义GlobalFilter

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

Ⅰ.定义

java">@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {log.info("pre...");return chain.filter(exchange).then(Mono.fromRunnable(()->{log.info("post...");}));}@Overridepublic int getOrder() {return Ordered.LOWEST_PRECEDENCE;}
}

Ⅱ.测试

从⽇志中,也可以看出来, 当GatewayFilter 和GlobalFilter 过滤器order⼀样时, 会先执⾏GatewayFilter.


http://www.ppmy.cn/embedded/147877.html

相关文章

【编辑器扩展】打开持久化路径/缓存路径/DataPath/StreamingAssetsPath文件夹

代码 [MenuItem("Assets/Open Explorer/PersistentDataPath")]public static void OpenPersistentDataPath(){Application.OpenURL(Application.persistentDataPath);}[MenuItem("Assets/Open Explorer/DataPath")]public static void OpenDataPath(){Appl…

大模型与呼叫中心结合的呼出机器人系统

大模型与呼叫中心结合的呼出机器人系统 原作者&#xff1a;开源呼叫中心FreeIPCC&#xff0c;其Github&#xff1a;https://github.com/lihaiya/freeipcc 随着人工智能技术的发展&#xff0c;特别是大模型&#xff08;large language models, LLMs&#xff09;的进步&#xf…

jdk17用jmap -hea打印JVM堆信息报错Cannot connect to core dump or remote debug server

文章目录 概述解决方法 概述 jdk17版本用jmap-heap查看堆信息&#xff0c;报错 Error: -heap option used Cannot connect to core dump or remote debug server. Use jhsdb jmap instead 解决方法 用命令 jhsdb jmap --heap --pid 代替

封装indexDB处理方法,重写为localStorage类似形式

indexDB简易化使用 一&#xff1a;场景 - 客户端数据持久化二&#xff1a;IndexedDBStorage类的创建三&#xff1a;使用方法 一&#xff1a;场景 - 客户端数据持久化 我的场景&#xff1a;资源超出localStorage最大存储容量&#xff0c;资源通过后台获取响应时间较长 二&#x…

后端接口返回文件流,前端下载(java+vue)

各位小伙伴们大家好&#xff0c;欢迎来到这个小扎扎的专栏 总结 | 提效 | 拓展&#xff0c;在这个系列专栏中记录了博主在学习期间总结的大块知识点&#xff0c;以及日常工作中遇到的各种技术点 ┗|&#xff40;O′|┛ ?? 内容速览 后端获取前端下载 本身前端是可以直接通过文…

基于SpringBoot+Vue的中药材进存销管理系统

系统展示 系统背景 在中医药行业&#xff0c;中药材的进存销管理是一项至关重要的工作。然而&#xff0c;传统的管理方式往往依赖于纸质记录和人工操作&#xff0c;这不仅效率低下&#xff0c;而且容易出现错误。随着信息化技术的不断发展&#xff0c;越来越多的企业开始寻求通…

宠物管理系统(2):utils

这篇文章介绍写这个系统中使用到的工具类。 验证码生成器 package com.wzb.utils.captcha;import java.util.Random;public class CaptchaUtils {// 定义包含数字和大小写字母的字符集private static final String CHAR_POOL "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn…

java版Spring Cloud+Mybatis+Oauth2+分布式+微服务+实现工程管理系统

鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工程管…