【Spring Cloud】统一服务入口-Gateway

【Spring Cloud】统一服务入口-Gateway

系列文章目录


文章目录


在这里插入图片描述

一、网关介绍

1.1 问题

之前的文章,我们通过Eureka, Nacos解决了服务注册, 服务发现的问题, 使用Spring CloudLoadBalance解决了负载均衡的问题, 使用OpenFeign解决了远程调用的问题.
但是当前所有微服务的接口都是直接对外暴露的, 可以直接通过外部访问. 为了保证对外服务的安全性,服务端实现的微服务接口通常都带有⼀定的权限校验机制. 由于使用了微服务, 原本⼀个应⽤的的多个模块拆分成了多个应用, 我们不得不实现多次校验逻辑. 当这套逻辑需要修改时, 我们需要修改多个应用, 加重了开发人员的负担.

针对以上问题, ⼀个常用的解决方案是使用API网关.

比如企业管理
外部人员去公司办理业务, 公司需要先核实对⽅的⾝份再去进⾏办理.
最开始只有⼀个员⼯, 这个员工核实之后直接办理即可. (单体架构)
随着公司的发展, 划分了多个部们, 每个部门负责的事情不同, 每个部门都需要先核实对方的身份再进行办理. (微服务架构)
这个流程存在⼀些问题:
1 . 办事效率低
2 . 增加了员工的工作流程
我们对此进行改进, 设立前台, 统⼀由前台来进行身份的校验. 前台⾝份校验通过后, 其他部门就设置信任, 直接办理.。

1.2 什么是API网关

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

在这里插入图片描述

网关核心功能:

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

1.3 常见网关实现

业界常用的网关方式有很多, 技术方案也较成熟, 其中不乏很多开源产品, 比如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倍.

二、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 编写启动类

@SpringBootApplicationpublicclassGatewayApplication{publicstaticvoidmain(String[] args){SpringApplication.run(GatewayApplication.class, args);}}

2.1.4 添加Gateway的路由配置

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

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/## - id: order-service uri: lb://order-service predicates:-Path=/order/** 

配置字段说明:

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

通过网关服务访问order-service的URL:http://127.0.0.1:10030/order/1

2.2 Route Predicate Factories

2.2.1 Predicate

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

@FunctionalInterfacepublicinterfacePredicate<T>{booleantest(T t);//...}

代码演示:

  1. 定义⼀个Predicate
classStringPredicateimplementsPredicate<String>{@Overridepublicbooleantest(String str){return str.isEmpty();}}
  1. 使用这个Predicate
publicclassPredictTest{publicstaticvoidmain(String[] args){Predicate<String> predicate =newStringPredicate();System.out.println(predicate.test(""));System.out.println(predicate.test("bite666"));}}
  1. Predicate的其他写法
    1)内置函数
publicclassPredictTest{publicstaticvoidmain(String[] args){Predicate<String> predicate =newPredicate<String>(){@Overridepublicbooleantest(String s){return s.isEmpty();}};System.out.println(predicate.test(""));System.out.println(predicate.test("bite666"));}}
  1. lambda写法:
publicclassPredictTest{publicstaticvoidmain(String[] args){Predicate<String> predicate = s -> s.isEmpty();System.out.println(predicate.test(""));System.out.println(predicate.test("bite666"));}}

Predicate predicate = s -> s.isEmpty(); 也可以写成
Predicate isEmpty = String::isEmpty;

  1. Predicate 的其他方法
• isEqual(Object targetRef) :比较两个对象是否相等,参数可以为Null
• and(Predicateother): 短路与操作,返回⼀个组成Predicate
• or(Predicate other):短路或操作,返回⼀个组成Predicate
• test(T t) :传入⼀个Predicate参数,用来做判断
• negate() :返回表示此Predicate逻辑否定的Predicate

2.2.2 Route Predicate Factories

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

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

具体可以参考相关网站:Spring Cloud Gateway

2.2.3 代码演示

  1. 添加Predicate规则
    在application.yml中添加如下规则
spring: cloud: gateway: routes: # ⽹关路由配置 - id: product-service #路由ID, ⾃定义, 唯⼀即可 uri: lb://product-service #⽬标服务地址 predicates: #路由条件 -Path=/product/## -After=2026-01-01T00:00:00.000+08:00[Asia/Shanghai]

这样我们可以通过修改时间访问限制,发现只有在时间限制范围内的请求可以通过。

2.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: 应用到所有的路由上, 也就是对所有的请求生效.

2.3.1 GatewayFilter

GatewayFilter 同 Predicate 类似, 都是在配置文件 application.yml 中配置,每个过滤器的逻辑都是固定的. 比如 AddRequestParameterGatewayFilterFactory 只需要在配置文件中写 AddRequestParameter , 就可以为所有的请求添加一个参数, 我们先通过⼀个例子来演示GatewayFilter如何使用
快速上手:

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, bite - id: order-service uri: lb://order-service predicates:-Path=/order/** 

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

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

@RequestMapping("/product")@RestControllerpublicclassProductController{@AutowiredprivateProductService productService;@RequestMapping("/{productId}")publicProductInfogetProductById(@PathVariable("productId")Integer productId,String userName){System.out.println("收到请求,Id:"+ productId);System.out.println("userName:"+ userName);return productService.selectProductById(productId);}}

GatewayFilter说明:

Spring Cloud Gateway提供了的Filter非常多,具体网站网址:网站

大家可以细化学习。

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

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

2.3.2 GlobalFilter

GlobalFilter是Spring Cloud Gateway中的全局过滤器, 它和GatewayFilter的作用是相同的.GlobalFilter 会应用到所有的路由请求上, 全局过滤器通常用于实现与安全性, 性能监控和日志记录等相关的全局功能.
Spring Cloud Gateway 内置的全局过滤器也有很多, 比如:
• Gateway Metrics Filter: 网关指标, 提供监控指标
• Forward Routing Filter: 用于本地forword, 请求不转发到下游服务器
• LoadBalancer Client Filter: 针对下游服务, 实现负载均衡.
• …
更多过滤器参考: 网址

快速上手

  1. 添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
  1. 添加配置
spring: cloud: gateway: metrics: enabled:true management: endpoints: web: exposure: include:"*" endpoint: health: show-details: always shutdown: 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 , 我们可以直接使用

2.5.1.1 定义GatewayFilter
@Slf4j@ServicepublicclassCustomGatewayFilterFactoryextendsAbstractGatewayFilterFactory<CustomGatewayFilterFactory.CustomConfig>implementsOrdered{publicCustomGatewayFilterFactory(){super(CustomConfig.class);}@OverridepublicGatewayFilterapply(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");}));});}@OverridepublicintgetOrder(){returnOrdered.LOWEST_PRECEDENCE;//配置优先级, order越⼤, 优先级越低}

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

@DatapublicclassCustomConfig{privateString name;}

代码说明:

类名统一以GatewayFilterFactory结尾, 因为默认情况下, 过滤器的name会采用该定义类的前缀. 这里的name=Custom(yml配置中使用)apply方法中, 同时包含Pre和Post过滤, then方法中是请求执行结束之后处理的CustomConfig 是一个配置类, 该类只有⼀个属性name, 和yml的配置对应该类需要交给Spring管理, 所以需要加 @Service 注解getOrder表示该过滤器的优先级, 值越大, 优先级越低
2.5.1.2 配置过滤器
spring: cloud: gateway: routes: # ⽹关路由配置 - id: product-service #路由ID, ⾃定义, 唯⼀即可 uri: lb://product-service #⽬标服务地址 predicates: #路由条件 -Path=/product/## filters:- name:Custom args: name: custom filter 

2.5.2 自定义GlobalFilter

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

2.5.2.1 定义GlobalFilter
@Slf4j@ServicepublicclassCustomGlobalFilterimplementsGlobalFilter,Ordered{@OverridepublicMono<Void>filter(ServerWebExchange exchange,GatewayFilterChain chain){ log.info("[Pre] CustomGlobalFilter enter...");return chain.filter(exchange).then(Mono.fromRunnable(()->{ log.info("[Post] CustomGlobalFilter return...");}));}@OverridepublicintgetOrder(){returnOrdered.LOWEST_PRECEDENCE;//配置优先级, order越⼤, 优先级越低}}

总结

以上就是本文全部内容,本文主要为大家介绍了Spring Cloud中统一服务入口的重要组件Gateway的相关操作和知识。感谢各位能够看到最后,如有问题,欢迎各位大佬在评论区指正,希望大家可以有所收获!创作不易,希望大家多多支持。

Read more

AIGC率检测哪个比较靠谱?我的血泪史和最终答案

凌晨两点,宿舍楼的灯光灭了大半,只有我的屏幕还亮着。那是我硕士论文提交前的最后一晚,我用某免费工具跑了一遍检测——78%的AI率。盯着那个数字,我手心冒汗,一遍遍刷新页面,希望是系统出错了。可结果纹丝不动。问题是,那篇论文我熬了三个星期,每个字都是自己敲的。后来我才知道,那晚让我崩溃的,不是我的论文有问题,而是我信错了工具。从那以后,我把市面上能试的AIGC检测平台几乎都测了一遍。今天,我想把这些踩过的坑和最终的答案,原原本本讲给你听。 一、为什么AIGC检测结果差这么多? 1. 不同工具的算法,各玩各的 关键要点: * 有的工具用统计特征分析,看词汇分布、句法复杂度 * 有的用深度学习模型,捕捉文本的语义连贯性 * 有的干脆就是黑箱,你永远不知道它凭什么判 我的亲身经历:有一次,我把一段纯手写的实验方法描述贴进三个工具。第一个显示78%,第二个23%,第三个直接说“无法判断”。我对着三个结果发了半天呆,最后还是不知道该怎么办。宿舍的台灯发着昏黄的光,我盯着屏幕上三个并排的窗口,手指无意识地敲着桌子。室友问我怎么还不睡,

By Ne0inhk
【GitHub Copilot】Figma MCP还原设计稿生成前端代码

【GitHub Copilot】Figma MCP还原设计稿生成前端代码

这里写自定义目录标题 * Step1:让AI给你配置MCP * Step2:替换成自己的Figma密钥 * Step3:如何使用 Cursor+Figma MCP的教程已经很多了,由于我所在的公司采购的是GitHub Copilot,我研究了一下直接在vscode里利用GitHub Copilot接入Figma MCP进行设计稿还原代码,大获成功,这里分享我的步骤,希望能帮到你。 Step1:让AI给你配置MCP 在vscode中打开你的项目(我的例子是一个微信小程序),呼出github copilot对话框,模式选择Agent,模型建议Claude 3.7 Sonnet,提问: https://github.com/GLips/Figma-Context-MCP 如何配置能让你在vscode里使用这个mcp 之后跟着提示狂点下一步即可完成配置,如果有什么需要装的vscode插件它会自动帮你装,甚至自动生成了配置说明文档。 由于不能保证AI每次生成的答案都一致,这里附上我的运行结果作为参考,可以看到它在项目文件夹最外层建了一个.vscode文件夹,在sett

By Ne0inhk
AI的提示词专栏:LLaMA-2 与 Mixtral 的提示词调优技巧

AI的提示词专栏:LLaMA-2 与 Mixtral 的提示词调优技巧

AI的提示词专栏:LLaMA-2 与 Mixtral 的提示词调优技巧 本文围绕 LLaMA-2 与 Mixtral 两大模型的提示词调优展开,先分析二者核心特性,再针对性给出适配原则与实战技巧。LLaMA-2 因参数规模差异大、通用领域训练数据为主、指令敏感度低,需按参数分层设计提示词、补充领域知识、强化指令约束,还提供了结构化指令、Few-Shot 示例等 5 个实战技巧;Mixtral 凭借混合专家架构、长上下文窗口、强多语言能力,需引导激活对应专家模块、合理处理长文本、规范多语言输出,配套专家引导指令等 4 个技巧。文章还对比二者调优重点与适用场景,指出常见误区并给出避坑方案,最后总结核心思路并提供后续实践建议,助力开发者优化提示词、发挥模型性能。 人工智能专栏介绍     人工智能学习合集专栏是 AI 学习者的实用工具。它像一个全面的 AI 知识库,把提示词设计、AI 创作、智能绘图等多个细分领域的知识整合起来。

By Ne0inhk
文心一言4.5开源模型测评:ERNIE-4.5-0.3B超轻量模型部署指南

文心一言4.5开源模型测评:ERNIE-4.5-0.3B超轻量模型部署指南

目录 * 引言:轻量化部署的时代突围 * 一.技术栈全景图:精准匹配的黄金组合 * 基础层:硬核环境支撑 * 框架层:深度优化套件 * 工具层:部署利器 * 二.详细步骤:精准匹配CUDA 12.6的黄金组合 * 准备环节 * 1.模型选择 * 2.配置实例 * 3.选择镜像 * 4.进入JupyterLab * 5.进入终端 * 6.连接到ssh * 系统基础依赖安装 * 1.更新源并安装核心依赖 * 2.安装 Python 3.12 和配套 pip * 解决 pip 报错 * 深度学习框架部署:PaddlePaddle-GPU深度调优 * FastDeploy-GPU企业级部署框架 * 1.安装FastDeploy核心组件 * 2.修复urllib3

By Ne0inhk