微服务-网关gateway理论与实战
网关
概述
网关作用显著,它能作为统一入口,整合后端服务,让客户端无需多次请求。
- 统一入口:整合后端多个服务,客户端只需调用网关提供的唯一接口,无需多次请求不同服务。
- 统一鉴权:集中处理所有服务的身份认证与访问权限控制,无需各服务单独处理。
- 服务发布控制:支持灰度发布等,能灵活控制流量分流到新旧服务,保障服务更新平稳。
- 协议转换:统一处理后端不同协议,对外提供一致的 HTTP 协议接口,简化客户端适配。
- 请求转发:接收客户端请求后,转发到对应的后端服务,还可实现内网与外网隔离。
- 限流、缓存:对请求进行限流,防止系统过载;缓存常用数据,提升响应速度。
鉴权认证
服务发布
灰度发布:新服务上线时,在网关层利用分流引擎
蓝绿发布
将新版本和旧版本都进行部署,然后将新版本上线,利用分流将所有请求换到新版本v2,当出现故障时,直接切回v1即可。
A / B Test
【使用于**测试新系统是否受欢迎**,如果发现不受欢迎则不上新服务】
新服务上线后,设定规则,只有符合规则的才可以访问到新服务(比如设置请求头Header的方法使得安卓用户可访问其他不行)
金丝雀/灰度 发布
刚开始将少量的请求引流到新版本上,然后后续再逐渐引入更多流量
实战
1. 路由匹配规则
- 时间匹配:通过
<font style="color:rgb(0, 0, 0);">Before</font>
/<font style="color:rgb(0, 0, 0);">After</font>
谓词,根据时间范围转发请求(如指定时间前 / 后路由到特定服务) - Cookie 匹配:
<font style="color:rgb(0, 0, 0);">Cookie</font>
谓词,根据请求中是否包含指定 Cookie 键值对进行路由 - Header 匹配:
<font style="color:rgb(0, 0, 0);">Header</font>
谓词,检查请求头中是否存在指定键及符合正则的 value(如<font style="color:rgb(0, 0, 0);">X-Request-Id</font>
为数字) - Host 匹配:
<font style="color:rgb(0, 0, 0);">Host</font>
谓词,根据请求头中的 Host 字段匹配(支持通配符,如<font style="color:rgb(0, 0, 0);">**.muse.com</font>
) - 请求方法匹配:
<font style="color:rgb(0, 0, 0);">Method</font>
谓词,仅允许指定 HTTP 方法(如 POST)的请求通过 - 路径匹配:
<font style="color:rgb(0, 0, 0);">Path</font>
谓词,按 URL 路径匹配(支持通配符,如<font style="color:rgb(0, 0, 0);">/gateway/**</font>
)
2. 过滤器功能
- 路径处理:
<font style="color:rgb(0, 0, 0);">StripPrefix</font>
,转发前移除路径前缀(如<font style="color:rgb(0, 0, 0);">StripPrefix=1</font>
移除第一个路径段) - 参数添加:
<font style="color:rgb(0, 0, 0);">AddRequestParameter</font>
,为请求添加固定查询参数(如统一添加<font style="color:rgb(0, 0, 0);">teacher=muse</font>
) - 响应头处理:
<font style="color:rgb(0, 0, 0);">AddResponseHeader</font>
,为响应添加固定 Header(如<font style="color:rgb(0, 0, 0);">X-Response-Teacher=Muse</font>
) - 限流控制:
<font style="color:rgb(0, 0, 0);">RequestRateLimiter</font>
,基于令牌桶算法限制请求频率(<font style="color:rgb(0, 0, 0);">replenishRate</font>
为令牌填充速度,<font style="color:rgb(0, 0, 0);">burstCapacity</font>
为令牌桶容量) - 请求重试:
<font style="color:rgb(0, 0, 0);">Retry</font>
,当后端返回指定状态码(如 500)时自动重试(<font style="color:rgb(0, 0, 0);">retries</font>
为重试次数) - 指标监控:
<font style="color:rgb(0, 0, 0);">GatewayMetricsFilter</font>
,通过配置 management 端点暴露网关监控指标 - 自定义过滤:通过
<font style="color:rgb(0, 0, 0);">name</font>
指定自定义过滤器类(需遵循<font style="color:rgb(0, 0, 0);">XXXGatewayFilterFactory</font>
命名规范),支持传入自定义参数
server:port: 8088
# 以下路由只能选一个#【请求路径匹配路由】
# 请求POST http://localhost:8088/gateway/say会转发http://localhost:8080/say。
spring:cloud:gateway:routes:- id: path_routeuri: http://localhost:8080 #访问地址predicates:- Path=/gateway/** # 路径匹配filters:- StripPrefix=1 # StripPrefix参数表示在将请求发送到下游之前从请求中剥离的路径个数。# 如果我们设置StripPrefix=2时,当通过网关向/gateway/bar/foo 发出请求时,对nameservice的请求将类似于/foo。#【指定时间规则匹配路由】
# 在2027-01-01 00:00:00之前访问的请求,都会转到http://localhost:8080,、
# 即:http://localhost:8088/say会转发到http://localhost:8080/say
spring:cloud:gateway:routes:- id: before_routeuri: http://localhost:8080 # 目标服务地址,请求最终转发的目的地predicates: # 只有当请求满足该路由的所有谓词条件时,才会触发路由转发。- Before=2027-01-01T00:00:00.000+08:00 # 即请求是符合再2027-01-01之前发送的,就符合该网关的转发,可以转发#【指定时间规则匹配路由】
#在2021-01-01 00:00:00之后访问的请求,都会转到http://localhost:8080,即:http://localhost:8088/say会转发到http://localhost:8080/say
spring:cloud:gateway:routes:- id: after_routeuri: http://localhost:8080predicates:- After=2021-01-01T00:00:00.000+08:00#【指定Cookie规则匹配路由】
#请求配置localhost的Cookie(chocolate=mic;) 请求http://localhost:8088/say会转发到http://localhost:8080/say
spring:cloud:gateway:routes:- id: cookie_routeuri: http://localhost:8080predicates:- Cookie=chocolate, mic # 即需要cookie中 chocolate = mic#【指定Header规则匹配路由】
#请求配置Header(key=X-Request-Id value=1) 请求http://localhost:8088/say会转发到http://localhost:8080/say
spring:cloud:gateway:routes:- id: header_routeuri: http://localhost:8080predicates:- Header=X-Request-Id, \d+#【指定Host规则匹配路由】
#请求配置Header(Host:www.muse.com) 请求http://localhost:8088/say会转发到http://localhost:8080/say
spring:cloud:gateway:routes:- id: host_routeuri: http://localhost:8080predicates:- Host=**.muse.com#【请求方法匹配路由】
# 请求POST http://localhost:8088/shout会转发http://localhost:8080/shout。请求GET http://localhost:8088/say 会报"404 Not Found"
spring:cloud:gateway:routes:- id: method_routeuri: http://localhost:8080 #访问地址predicates:- Method=POST#【请求路径匹配路由】
# 请求POST http://localhost:8088/gateway/say会转发http://localhost:8080/say。
spring:cloud:gateway:routes:- id: path_routeuri: http://localhost:8080 #访问地址predicates:- Path=/gateway/** #路径匹配filters:- StripPrefix=1 #StripPrefix参数表示在将请求发送到下游之前从请求中剥离的路径个数。如果我们设置StripPrefix=2时,当通过网关向/gateway/bar/foo
# 发出请求时,对nameservice的请求将类似于/foo。#【添加request查询参数过滤器】
#会对所有请求增加teacher=muse这个参数
spring:cloud:gateway:routes:- id: add_request_parameter_routeuri: http://localhost:8080 #访问地址predicates:- Path=/**filters:- AddRequestParameter=teacher, muse#【添加response响应Header参数过滤器】
#返回结果给客户端之前,在Header中添加相应的数据
spring:cloud:gateway:routes:- id: add_response_header_routeuri: http://localhost:8080 #访问地址predicates:- Path=/**filters:- AddResponseHeader=X-Response-Teacher, Muse#【添加限流过滤器】
spring:cloud:gateway:routes:- id: request_ratelimiter_routeuri: http://localhost:8080 #访问地址predicates:- Path=/**filters:- name: RequestRateLimiterargs:redis-rate-limiter.replenishRate: 1 #令牌桶中令牌的填充速度,代表允许每秒执行的请求数redis-rate-limiter.burstCapacity: 1 #令牌桶的容量,也就是令牌桶最多能够容纳的令牌数。表示每秒用户最大能够执行的请求数量
redis:host: 127.0.0.1port: 6379#【添加请求重试过滤器】
spring:cloud:gateway:routes:- id: retry_routeuri: http://localhost:8080predicates:- Path=/example/**filters:- name: Retryargs:retries: 3 #请求重试次数,默认值是3status: 500 #HTTP请求返回的状态码,针对指定状态码进行重试。- StripPrefix=1#【网关指标过滤器——GatewayMetricsFilter】
management:endpoint:gateway:enabled: trueendpoints:web:exposure:include: "*"
自定义过滤器
#【请求路径匹配路由——配置自定义过滤器】
spring:cloud:gateway:routes:- id: define_filteruri: http://localhost:8080 #访问地址predicates:- Path=/gateway/** #路径匹配filters:- name: GpDefine # 自定义过滤器的名字,即:GpDefineGatewayFilterFactoryargs:name: Gp_Mic #GpConfig.getName这个值- StripPrefix=1 #跳过前缀
自定义过滤器:类名必须命名为 xxGatewayFilterFactory
xx 即为 name,这个 name 需要到 yaml 中配置,该过滤器才会生效【如下面的GpDefine】
作用与使用场景:
- 作用:仅对配置了该过滤器的路由生效,实现路由级别的个性化处理。
- 使用场景:
- 特定路由的日志记录(如支付路由单独记录请求参数)。
- 路由级别的参数校验(如仅对用户路由验证 token 格式)。
- 路由专属的响应加工(如对商品路由的响应添加促销标签)。
package com.muse.gatewaysample;import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;/*** 自定义路由级别GatewayFilter* 注意:* 1. 类名必须以GatewayFilterFactory结尾,前缀(GpDefine)作为过滤器在yaml中的引用名称* 2. 需继承AbstractGatewayFilterFactory,泛型指定配置类类型* 3. 通过@Service注解注册到Spring容器中*/
@Service
public class GpDefineGatewayFilterFactory extends AbstractGatewayFilterFactory<GpDefineGatewayFilterFactory.GpConfig> {/*** 构造方法:必须调用父类构造器,传入配置类Class对象* 用于告诉父类当前过滤器使用哪个配置类解析yaml参数*/public GpDefineGatewayFilterFactory() {super(GpConfig.class);}/*** 核心方法:实现过滤逻辑* @param config 配置类对象,包含从yaml中读取的参数* @return GatewayFilter 过滤器实例*/@Overridepublic GatewayFilter apply(GpConfig config) {// 返回一个GatewayFilter函数式接口实现return ((exchange, chain) -> {// 【前置过滤逻辑】:在请求转发到后端服务之前执行// exchange包含请求和响应对象,可获取/修改请求参数、头信息等System.out.println("GpDefineGatewayFilterFactory [Pre] Filter Request, config.getName() = " + config.getName());// 继续执行过滤器链,chain.filter(exchange)会将请求传递给下一个过滤器// then()方法中的逻辑会在后端服务响应后执行(后置处理)return chain.filter(exchange).then(Mono.fromRunnable(() -> {// 【后置过滤逻辑】:在后端服务返回响应后、响应给客户端之前执行System.out.println("GpDefineGatewayFilterFactory [Post] Response Filter");}));});}/*** 自定义配置类:用于接收yaml中配置的过滤器参数* 配置类的属性会与yaml中args下的参数自动绑定*/public static class GpConfig {// 示例参数:nameprivate String name;// 必须提供getter和setter方法,否则无法完成参数绑定public String getName() {return name;}public void setName(String name) {this.name = name;}}
}
全局过滤器(GpdefineFilter)
去实现了全局过滤器,每个请求都会经过这个,无需配置即可使用(使用场景)
特点(与自定义过滤器的核心区别)
- 无需 YAML 配置:所有请求都会经过,不依赖路由配置。
- 全局生效:无论匹配哪个路由,都会执行该过滤器。
作用与使用场景
- 作用:对整个网关的所有请求 / 响应进行统一处理。
- 使用场景:
- 全局鉴权(如验证所有请求的 token 是否存在)。
- 全局限流(如限制所有请求的 QPS)。
- 全局日志收集(记录所有请求的 URL、耗时等)。
- 跨域处理(统一配置 CORS 规则)。
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component // 注册到容器
public class GpDefineFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, org.springframework.cloud.gateway.filter.GatewayFilterChain chain) {// 前置处理:所有请求都会经过这里(如全局鉴权)System.out.println("GlobalFilter [Pre] 全局前置处理");return chain.filter(exchange).then(Mono.fromRunnable(() -> {// 后置处理:所有响应返回前都会经过这里(如全局响应日志)System.out.println("GlobalFilter [Post] 全局后置处理");}));}@Overridepublic int getOrder() {return 0; // 执行顺序(0优先于1)}
}
nacos集成gateway
作用和使用场景Nacos 作为注册中心和配置中心,与 Gateway 集成的核心价值是 实现服务的动态发现、路由管理和配置动态更新,解决网关对后端服务的动态适配问题。
具体体现:
- 服务发现与动态路由:Gateway 通过 Nacos 自动感知后端服务(Provider)的注册、下线、地址变更,无需手动修改路由的
uri
配置。 - 负载均衡:结合
lb://服务名
路由格式,Gateway 可基于 Nacos 提供的服务列表实现请求的负载均衡(如轮询、权重等)。 - 配置动态刷新:若网关路由规则配置在 Nacos 配置中心,修改后可实时生效,无需重启网关服务。
使用场景
- 微服务架构中的服务路由:当系统存在多个后端服务(如用户服务、订单服务),且服务实例会动态扩缩容时,通过 Nacos 让 Gateway 自动发现服务并转发请求。
- 多环境适配:开发、测试、生产环境的服务地址不同,通过 Nacos 统一管理服务注册,Gateway 无需修改配置即可适配不同环境。
- 灰度发布 / 蓝绿部署:配合 Nacos 的权重配置,Gateway 可将部分流量路由到新版本服务实例,实现平滑更新。
需要nacos-gateway-provider(作为转发的地址)、nacos-gateway-consumer(作为直接地址)
1.consumer-yaml
server:port: 8888 # Consumer端端口(网关服务运行端口,客户端通过此端口访问网关)spring:application:name: gateway-nacos-consumer # 服务名(注册到Nacos的名称,用于标识网关服务)cloud:nacos:discovery:server-addr: 127.0.0.1:8848 # Nacos注册中心地址(网关服务需注册到Nacos,才能发现Provider服务)gateway:discovery:locator:enabled: true # 开启「从Nacos注册中心动态创建路由」的功能(无需手动配置每个Provider路由,可通过服务名访问)lower-case-service-id: true # 允许使用小写的服务名访问(默认服务名是大写,开启后可通过 http://网关IP:端口/服务名/接口 访问)routes: # 手动配置的路由规则(优先级高于动态路由)- id: gateway-nacos-provider # 路由唯一标识(自定义,需唯一)uri: lb://gateway-nacos-provider # 路由目标地址:lb=Load Balance(负载均衡),后面跟Provider在Nacos的服务名predicates: # 路由匹配条件(满足才触发此路由)- Path=/nacos/** # 路径匹配:客户端请求路径以 /nacos 开头时,触发此路由filters: # 路由过滤器(对请求/响应做加工)- StripPrefix=1 # 移除路径前缀1级:如请求 /nacos/sayHello → 转发给Provider时路径变为 /sayHello
2.provider-yaml
server:port: 8080 # Provider端端口(业务服务运行端口,处理实际业务逻辑)spring:application:name: gateway-nacos-provider # 服务名(注册到Nacos的名称,Consumer端通过此名发现该服务)cloud:nacos:discovery:server-addr: 127.0.0.1:8848 # Nacos注册中心地址(将Provider服务注册到Nacos,供网关发现)
package com.muse.gateway.controller;import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;/*** Provider端业务控制器:处理客户端通过网关转发过来的请求*/
@RestController // 标识此类为REST接口控制器(返回JSON数据,无页面)
public class NacosController {// 从配置文件中读取 server.port 属性值(即当前Provider服务的运行端口)@Value("${server.port}")private int port;/*** 业务接口:处理GET请求,返回带端口的问候信息* 访问路径:Provider端原生路径是 /sayHello,通过网关访问时需加前缀 /nacos(对应Consumer路由配置)*/@GetMapping("/sayHello") // 映射GET请求到 /sayHello 路径public String sayHello() {// 控制台打印日志(用于调试,确认请求到达Provider)System.out.println("[gateway-nacos-provider]: sayHello");// 返回响应结果(包含端口,方便区分多实例部署时的负载均衡效果)return "[gateway-nacos-provider]: sayHello port=" + port;}
}
Sentinel 集成 Gateway 实现
作用和使用场景核心作用
Sentinel 作为流量治理工具,与 Gateway 集成的核心价值是 对网关层的请求进行流量控制、熔断降级和监控,保护后端服务不被流量冲击。
具体体现:
- 网关层限流:基于路由、路径、参数等维度限制 QPS(如限制
/api/order/**
路径每秒最多 100 个请求)。 - 熔断降级:当后端服务响应缓慢或异常时,Sentinel 可触发熔断,直接返回降级响应(如 “服务繁忙”),避免请求堆积拖垮服务。
- 流量监控:通过 Sentinel 控制台实时查看网关的请求量、限流次数、响应时间等指标,便于问题排查。
使用场景
- 高并发接口保护:针对秒杀、促销等场景的核心接口(如
/api/seckill/**
),通过限流防止流量超过服务承载能力。 - 服务稳定性保障:当后端服务出现异常(如数据库超时),通过熔断机制快速失败,避免网关持续转发无效请求。
- 流量监控与告警:实时监控网关流量趋势,当请求量突增或限流频繁时,通过 Sentinel 告警及时介入处理。
二、代码与配置
1. 公共依赖(两个服务都需要)
xml
<!-- pom.xml 核心依赖 -->
<dependencies><!-- Spring Cloud Gateway --><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><!-- Sentinel 集成 Gateway --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId></dependency><!-- Sentinel 核心包 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
</dependencies>
2. Provider 端(sentinel-gateway-provider,业务服务)
(1)application.yml 配置
server:port: 8080 # Provider服务端口spring:application:name: sentinel-gateway-provider # 服务名(注册到Nacos的标识)cloud:nacos:discovery:server-addr: 127.0.0.1:8848 # Nacos注册中心地址(可选,不使用注册中心可删除)
(2)业务控制器(ProviderController.java)
package com.muse.sentinel.controller;import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;/*** Provider业务接口:处理网关转发的请求*/
@RestController
public class ProviderController {// 注入当前服务端口,用于区分实例@Value("${server.port}")private int port;/*** 测试接口:返回带端口的响应*/@GetMapping("/test")public String test() {System.out.println("Provider接收请求,端口:" + port);return "Provider响应 [port=" + port + "]";}
}
3. Consumer 端(sentinel-gateway-consumer,网关服务)
(1)application.yml 配置
yaml
server:port: 8888 # 网关服务端口(客户端访问入口)spring:application:name: sentinel-gateway-consumer # 网关服务名cloud:nacos:discovery:server-addr: 127.0.0.1:8848 # Nacos地址(可选)gateway:routes:- id: provider_route # 路由唯一标识uri: lb://sentinel-gateway-provider # 转发目标(lb=负载均衡,服务名对应Provider)predicates:- Path=/api/** # 匹配路径:客户端访问/api/** 时触发此路由filters:- StripPrefix=1 # 移除1级前缀:/api/test → 转发为/testsentinel:transport:dashboard: 127.0.0.1:8080 # Sentinel控制台地址(需单独启动)port: 8719 # 本地与Sentinel控制台通信的端口(默认8719,如被占用自动+1)scg:fallback:mode: response # 限流/熔断后的降级模式(response=返回固定响应)response-body: "请求过于频繁,请稍后再试!" # 降级响应内容
(2)网关配置类(SentinelGatewayConfig.java)
package com.muse.sentinel.config;import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashSet;
import java.util.Set;/*** Sentinel网关配置:注册过滤器并初始化限流规则*/
@Configuration
public class SentinelGatewayConfig {/*** 注册Sentinel全局过滤器:所有请求都会经过此过滤器,实现限流/熔断*/@Beanpublic GlobalFilter sentinelGatewayFilter() {return new SentinelGatewayFilter();}/*** 初始化Sentinel限流规则(也可通过Sentinel控制台动态配置)*/public void initGatewayRules() {Set<GatewayFlowRule> rules = new HashSet<>();// 为路由id=provider_route设置限流规则rules.add(new GatewayFlowRule("provider_route").setCount(2) // 限流阈值:每秒最多2个请求.setIntervalSec(1) // 统计时间窗口(秒));// 将规则加载到SentinelGatewayRuleManager.loadRules(rules);}
}