当前位置: 首页 > news >正文

SpringCloud GateWay网关

1、网关介绍

        微服务网关(Microservices Gateway)是微服务架构中的核心组件,充当所有客户端请求的统一入口,负责请求的路由、过滤和聚合等操作。它是微服务与外部系统(如Web、移动端)之间的中间层,简化了客户端的调用复杂度,并提供了统一的治理能力。

2、网关的作用

        网关的作用有:统一入口、请求路由、负载均衡、流量控制、身份认证、协议转换、系统监控、安全防护。  

1、统一入口

        统一入口,也叫路由转发。将客户端请求动态路由到对应的微服务实例(如 /order 请求转发到订单服务)。并且支持路径重写、负载均衡。前端不需要知道每一个后端服务的地址,只要知道网关地址就行了。在企业系统中,会统一通过api网关对外暴露接口,屏蔽内部服务拆分带来的复杂性。比如对于财务模块,接口都是以/api/fc/xxx的形式。有/fc/AAA,/fc/BBB,/fc/CCC,这些接口可能部署在不同的服务器上,那么对应的ip地址也不一样。有了网关的话,就不用记住那么多ip地址了。都走网关就行了。确保前端只与网关通信,无需感知后端服务地址。

2、请求路由

        根据请求特征(如URL路径、HTTP头、参数)将请求精准分发至对应后端服务,支持动态路由策略。静态路由通常是预定义以硬编码的形式写在配置文件yml中的。项目启动后,静态路由就生效了,如果想要修改的话,需要重启项目。而动态路由是运行时加载,可以写在数据库中,或者配置中心apollo,运行的时候读取配置。

3、负载均衡

        通过算法(如轮询、加权轮询、最少连接)将流量分发到多个服务器实例,避免单点过载,提升系统可用性。在一些高并发的场景中,通过网关将请求分散至多台服务器,防止单台服务器宕机。

4、流量控制

        通过限流(Rate Limiting)、熔断(Circuit Breaking)和降级(Degrade)策略,防止系统过载导致服务中断,保障核心服务稳定。

        限流:可以通过控制请求速率防止服务过载。比如,可以使用网关内置限流,使用Redis+Lua脚本实现分布式限流。Redis作为分布式计数器存储,确保多节点限流一致性。Lua脚本保证检查+更新计数的原子性操作。

        熔断:当下游服务响应超时或报错时,快速失败避免资源耗尽。防止一个服务的故障扩撒到整个系统,产生雪崩效应。可以使用Hystrix实现服务熔断。Hystrix实现服务熔断其实就是有点类似于java里面的异常处理机制。

        降级:一旦程序发生异常则直接将当前服务熔断,并把发生异常的服务降级(前提是要有次级服务)。一级降二级,二级降三级......依次类推。通常会采用多级降级策略:

        一级降级(轻度):

        场景:服务器资源使用率达到70%

        动作:关闭一些非核心任务:日志分析、数据备份、广告推送等。

        降低服务质量:降低图片视频的清晰度。

        启用本地缓存:一些页面读取本地缓存信息,而不是直接读取远程的信息。

        二级降级(中度):

        场景:服务器资源使用率达到90%

        动作:关闭非核心API,简化业务流程,同步改异步(实时性改为非实时性,延迟写入数据

        库,先存内存队列)。

        三级降级(重度):

        场景:服务器资源耗尽

        动作:仅保留核心功能,如电商系统仅保留下单、支付功能。商品详情页仅返回静态HTML

        (无实时数据),用户信息返回预 设默认值(如匿名用户头像)。进入安全模式,拒绝所有

        的写操作,关闭第三方服务调用(短信通知),启用本地事 务日志。

        实现服务熔断、降级,可以引入Hystrix,在相应的接口上加上:@HystrixCommand(fallbackMethod = "AAA")。

        如以下三个服务:AAA、BBB、CCC。AAA发生故障,会进行熔断并降级到BBB,如果BBB再发生故障,会熔断并降级到CCC。

@GetMapping("/AAA")
@ResponseBody
@HystrixCommand(fallbackMethod = "BBB")
public float AAA() {......
}@HystrixCommand(fallbackMethod = "CCC")
public float BBB() {......
}public float CCC() {......
}

 

5、身份认证

        在企业内网中,用户通过网关输入账号密码去访问内部的OA系统。在调用内部服务API时,也会经过网关,网关校验token,确保调用方权限。

6、协议转换

        在不同协议间转换数据格式,如Http转Https、WebSocket、Modbus转OPC UA,实现异构系统互联。

        Http:超文本传输协议,主要用于Web/移动端/微服务,是明文传输,通常用于测试环境,默认端口是80。

        Https:在Http的基础上进行了加密,即:Http+SSL/TLS,SSL(安全套接层协议)是用于网络通信中数据加密身份验证。数据传输时通过SSL进行加密,并通过数字证书验证服务器身份,确保用户访问的是真实网站,而非假冒伪劣网站。SSL通常是由权威机构CA签发的。默认端口443。

        WebSocket:全双工长连接。实时双向通信。主要用于:实时聊天、在线协作、网游中多玩家。WS默认端口80,WSS默认端口443。

        Modbus:该协议用于工业设备,如PCL控制。

        OPC UA:该协议是一种面向服务的工业通信框架。

7、系统监控

        收集请求日志,实时监控系统状态,辅助故障排查与优化。

8、安全防护

        网关可以提供访问控制、流量过滤、加密通信等安全功能。

3、实现一个网关

        我使用的是Spring Cloud Gateway。不过也可以用Nginx作为网关。因为SpringCloudGateway是java写的,运行在JVM上,因此对于基于SpringCloud框架的微服务来说,SpringCloudGateway更适用。

        对于非SpringCloud框架的服务来说,使用Nginx更适合了。Nginx是基于C语言开发的,性能比SpringCloud更好。Nginx支持反向代理,所谓反向代理就是代理服务端去接收客户端的请求,并分发给对应的内部服务。网关起的就是一个反向代理的作用。隐藏服务细节,客户端无需感知后端服务地址。Nginx通常会用于高并发的场景,Nginx作为入口网关,处理静态资源请求,并将动态请求转发到后端应用服务器(如Tomcat、Node.js),同时实现SSL终止、缓存、负载均衡等功能。

        正向代理就是代理客户端向服务端发起请求。通常用于各种VPN。

3.1、创建一个Eureka注册中心

        Step1、引入eureka依赖 pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>eureka-center</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><springframework.version>1.5.4.RELEASE</springframework.version><springframework.version1>1.3.5.RELEASE</springframework.version1></properties><dependencies><!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-eureka-server</artifactId><version>${springframework.version1}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>${springframework.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>${springframework.version}</version><scope>test</scope></dependency></dependencies></project>

        Step2、application.yml

server:port: 8001#Eureka配置
eureka:instance:hostname: localhost  #Eureka服务端的实例名称client:register-with-eureka: false  #是否向eureka注册中心注册自己,因为这里本身就是eureka服务端,所以无需向eureka注册自己fetch-registry: false #fetch-registry为false,则表示自己为注册中心service-url:   #监控页面defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

        Step3、启动类

package com.eureka;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;/*** @author: Wulc* @createTime: 2025-05-02* @description:* @version: 1.0*/@SpringBootApplication
@EnableEurekaServer  //使eureka服务端可以工作
public class SpringcloudEurekaApplication {public static void main(String[] args) {SpringApplication.run(SpringcloudEurekaApplication.class, args);}
}

        项目启动成功后,浏览器输入:http://localhost:8001/ 一个Eureka注册中心就已经好了。

 

3.2、创建一个网关服务

        Step1、引入gateway依赖 ​​​​​ pom.xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>my-gateway</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.3</version><relativePath/> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId><exclusions><!-- 排除可能引入的Spring MVC依赖 -->
<!--                Spring Cloud Gateway基于WebFlux响应式框架(非阻塞式),而Spring MVC是传统的Servlet-based框架(阻塞式)。-->
<!--                当两者同时存在于classpath时,Spring Boot无法决定使用哪种Web服务器(Tomcat vs Netty),导致启动失败。--><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></exclusion></exclusions></dependency><!--        loadbalancer是负载均衡,对应yml中的lb--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency></dependencies><!--    使用dependencyManagement统一管理SpringCloud组件,集中定义所有SpringCloud相关组件的兼容版本,避免手动指定每个依赖的版本号,--><!--    解决版本冲突问题。我这里使用了2021.0.3,对应的是Springboot2.6.x--><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>2021.0.3</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

         Step2、编写yml文件 application.yml

server:port: 80spring:main:web-application-type: reactive  # 强制使用WebFluxapplication:name: my-gateway-serviceprofiles:include: route  #使用application-route.yml里面的配置eureka:client:service-url:defaultZone: http://localhost:8001/eureka/  # Eureka注册中心地址register-with-eureka: truefetch-registry: trueinstance:prefer-ip-address: trueinstance-id: ${spring.application.name}:${server.port}

        application-route.yml

spring:cloud:gateway:discovery:locator:enabled: true  # 开启从注册中心动态创建路由lower-case-service-id: true  # 服务名小写routes:- id: route1uri: lb://wulc-test-consumer-server  # lb表示负载均衡 loadbalancepredicates: #断定,遵守哪些规则,就把请求转发给wulc-test-consumer-server这个服务- Path=/api/wulc/**- id: route2uri: lb://wulc-test-serverpredicates:- Path=/api/test/**filters:#StripPrefix=1表示移除请求路径中的第1个路径,#即:如果前端的请求是/api/test/getMsg,那么gateway网关转发时就会找/test/getMsg- StripPrefix=1		order: 0

        Step3、启动类

package com.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;/*** @author Wulc* @date 2025/5/4 13:56* @description*/
@SpringBootApplication
@EnableDiscoveryClient
public class ApplicationStarter {public static void main(String[] args) {SpringApplication.run(ApplicationStarter.class, args);}
}

        启动成功后,在eureka上服务注册成功了。

3.3、创建一个普通服务

        Step1、引入相关依赖 pom.xml

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>wulc-test</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><name>wulc-test</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><springframework.version>1.5.4.RELEASE</springframework.version><springframework.version1>1.3.5.RELEASE</springframework.version1></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>${springframework.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>${springframework.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>${springframework.version}</version></dependency><!--        eureka--><!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-eureka</artifactId><version>${springframework.version1}</version></dependency></dependencies></project>

        Step2、yml文件 application.yml

server:port: 8084spring:application:name: wulc-test-server#eureka配置,服务注册到哪?
eureka:client:service-url:defaultZone: http://localhost:8001/eureka/instance:#修改eureka上默认描述信息instance-id: ${spring.application.name}:${server.port}

        Step3、controller

package com.test.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author: Wulc* @createTime: 2025-05-01* @description:* @version: 1.0*/@RestController
@RequestMapping("/test")
public class MsgController {@GetMapping("/getMsg")public String getMsg(){System.out.println("成功获取信息");return "成功获取信息";}
}

        Step4、启动类

package com.test;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;/*** @author: Wulc* @createTime: 2025-05-01* @description:* @version: 1.0*/
@EnableDiscoveryClient
//@EnableEurekaClient
@SpringBootApplication
public class ApplicationStarter {public static void main(String[] args) {SpringApplication.run(ApplicationStarter.class, args);}
}

        注意:@EnableDiscoveryClient和@EnableEurekaClient都是用于将微服务注册到服务注册中心的注解。但@EnableEurekaClient仅支持Eureka,而@EnableDiscoveryClient同时支持:Eureka、Consul、Zookeeper、Nacos 等。

        启动成功后,在Eureka上注册了该服务。

  

        通过网关,成功转发到了wulc-test-server服务上的/test/getMsg接口。

        我们可以分别用8085、8086端口号再启动两个服务,作为集群,来验证负载均衡。

 

         使用postman访问网关接口:http://localhost:80/api/test/getMsg 33次。可以看到gateway网关,将33次请求转发到了wulc-test-server:8084、wulc-test-server:8085、wulc-test-server:8086三个服务上面,起到了一个负载均衡的作用。

 

4、网关跨域

        所谓跨域,是指不同源的客户端/服务端,在没有对方授权的情况下是不允许发送/接收对方的数据资源的,会产生“跨域”情况。“跨域”是浏览器的一种保护机制,是由同源策略所导致的限制。所谓同源策略指的是:只有当两个url协议、域名和端口完全一致时,才认为是同源,否则就是跨域。跨域一般是在前端访问后端接口时产生的。在前端服务器上可以ping通相应的后端服务器,但是由于前后端服务器不同源,因此产生跨域无法访问。

 

        常见的跨域解决方法有,在Springboot控制类或者方法上加@CrossOrigin注解。

        @CrossOrigin注解有:value、origins、allowedHeaders、exposedHeaders、methods、allowCredentials、maxAge这些属性。

(1)value和origins是等价的,用于指定允许访问资源的来源,例:

// 允许单个源
@CrossOrigin(origins = "http://localhost:3000")// 允许多个源
@CrossOrigin(origins = {"http://site1.com", "https://site2.com"})// 允许所有源(慎用,生产环境不推荐)
@CrossOrigin(origins = "*")

(2)allowedHeaders,定义客户端可以在请求中携带的 HTTP 头字段(如 `Authorization`、`Content-Type`)。默认仅允许简单头(`Accept`、`Accept-Language`、`Content-Language`、`Content-Type`)。例:

// 允许自定义头
@CrossOrigin(allowedHeaders = {"X-Custom-Header", "Authorization"})// 允许所有头(开放权限,慎用)
@CrossOrigin(allowedHeaders = "*")

(3)exposedHeaders,指定哪些响应头可以被浏览器访问(默认只能读取简单响应头)。例:

// 暴露自定义头
@CrossOrigin(exposedHeaders = "X-Total-Count")// 暴露多个头
@CrossOrigin(exposedHeaders = {"X-Header1", "X-Header2"})

 (4)methods,限制允许的http方法,如get、post。例:

// 只允许 GET 和 POST
@CrossOrigin(methods = {RequestMethod.GET, RequestMethod.POST})// 允许所有方法(开放权限,慎用)
@CrossOrigin(methods = "*")

(5)allowCredentials,是否允许凭据,用于控制是否允许浏览器发送凭据(如 CookieAuthorization 头)。默认 false(不允许),设为 true 时需配合 origins 明确指定来源(不能为 *)。例:

// 允许携带 Cookie
@CrossOrigin(allowCredentials = "true", origins = "http://trusted-site.com")
fetch("http://your-api.com/data", {credentials: "include"  // 发送 Cookie
});

(6)maxAge,预检请求缓存时间。所谓缓存预检是浏览器在有效时间内,不会重复对同一跨域请求进行检查,直接发送真实的请求。即,在第一次发送跨域访问时,浏览器会检查跨域请求的合法性。如果合法的话,那么在设置的maxAge时间内,如果是同一请求方再次发起跨域访问,浏览器直接跳过跨域合法性检查阶段。从而提升性能。例:

@CrossOrigin(origins = "http://xxx.com", maxAge = 3600)		maxAge单位为秒,即3600秒

        当然因为在实际中,前后端交互会涉及很多的服务和接口。如果一个服务一个服务,一个接口一个接口,一个控制类一个控制类的,去加@CrossOrigin,去各种配置,写CorsConfiguration类的话,会很麻烦,不便于统一管理。因此,我们可以统一在网关这层进行全局跨域规则配置。统一管理所有后端服务的跨域配置。

        我这里给一个简单的网关跨域yml配置。详细的配置方法可以参考一下文档:Spring Cloud Gateway 中文文档 或者问一下AI。

spring:cloud:gateway:globalcors:cors-configurations:'[/**]':allowed-origin-patterns: '*'  #允许所有的跨域allowed-headers: '*'  #允许所有的头allowed-methods: '*'  #允许所有的请求方式

5、总结

        其实SpringCloud Gateway网关的核心就是:路由、断言、过滤器。路由就是告诉网关该转发到哪个对应的后端服务上,断言就是转发规则,过滤器就是对请求api的过滤,比如前端调用后端接口,都会加一个/api前缀用于区分接口的类型(普通接口、feign接口)。但是在写后端接口时,通常不会在Controller里面@RequestMapping上加上/api,因此需要配置过滤器,网关转发请求时,把前缀去掉。网关通常可以用于身份认证。利用网关进行身份认证有两种方式:一种是在网关层直接校验token,另一种是网关将认证请求转发到专门的鉴权服务,由专门的鉴权服务校验token,并把校验结果返回网关,如果有权限,则网关再转发到相应的后端服务上。

        网关直接校验token(推荐使用这种)

 

        网关转发给鉴权服务校验token(比较复杂)

 

        微服务之间的调用,默认是不经过网关的。直接去注册中心,找到要调用的服务在哪里就行了,通过feign调用。

        微服务之间的调用,也是可以经过网关的。虽然从技术上可以实现微服务之间调用经过网关,但是实际中不建议经过网关,网关是对接前端的,后端之间的微服务调用,直接调用就行了,使用feign调用。这样还省了一层路径。除非外部后端系统要调用你这个服务的接口,最好加一层网关,进行身份认证。

        在典型的微服务架构中,无论是前端的请求,还是后端对前端请求的响应,都必须经过网关。

         为什么不能绕过网关?

6、参考资料

63、Gateway - 总结哔哩哔哩bilibili

基于Eureka的网关服务(gateway)配置-CSDN博客

SpringCloud微服务中gateway网关的使用(一)——(介绍+gateway与zuul的区别+gateway实现的两种方式)_SpringCloud-CSDN专栏

springcloud-eureka与gateway简易搭建_gateway注册到eureka-CSDN博客

网关 GateWay 的使用详解、路由、过滤器、跨域配置_gateway配置路由转发-CSDN博客

注解@CrossOrigin解决跨域问题 - 淼淼之森 - 博客园

文心一言

DeepSeek - 探索未至之境

 

http://www.xdnf.cn/news/288091.html

相关文章:

  • 排序用法(Arrays.sort)
  • AI笔记-1
  • Qwen2_5-Omni-3B:支持视频、音频、图像和文本的全能AI,可在本地运行
  • 【Flask】ORM模型以及数据库迁移的两种方法(flask-migrate、Alembic)
  • 【全队项目】智能学术海报生成系统PosterGenius--前后端系统介绍
  • Vuex使用指南:状态管理
  • Leetcode:回文链表
  • GGD独立站的优势
  • 备战蓝桥杯国赛第一天-atcoder-beginner-contest404
  • Python异步编程进阶:深入探索asyncio高级特性
  • 从零开始开发纯血鸿蒙应用之NAPI
  • Linux的web服务器的部署及优化
  • 关于浏览器页面自动化操作
  • Python 矩阵运算:从理论到实践
  • 五大神经网络开发实战:从入门到企业级部署
  • 《Python星球日记》第30天:Flask数据库集成
  • 虚幻基础:硬件输入
  • 蓝桥杯 19. 植树
  • 【题解-洛谷】B4303 [蓝桥杯青少年组省赛 2024] 字母移位
  • [HOT 100] 2538. 最大价值和与最小价值和的差值
  • LabVIEW伺服电机故障监测系统
  • 【QT】QT中的事件
  • JavaSE笔记--反射篇
  • Cron表达式的用法
  • cudaMalloc函数说明
  • 5.5刷题map和set的使用
  • 笔试专题(十五)
  • 3小时超快速入门Python
  • 字符串,数组,指针之间的关系
  • Python实现自动驾驶中的车道检测算法:从理论到实践