本文基于上一篇http://t.csdnimg.cn/q3YrK 使用抽取的方案使用feign的基础上使用Spring Cloud Gateway。
API网关
API网关(简称网关)也是一个服务,通常是后端服务的唯一入口。它就像是整个微服务架构的门面,所有的外部客户端访问都需要经过它来进行调度和过滤。
网关核心功能
权限控制:作为微服务的入口,对用户进行权限校验,如果校验失败则进行拦截。
动态路由:一切请求先经过网关,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务。
负载均衡:当路由的目标服务有多个时,还需要做负载均衡。
限流:请求流量过高时,按照网关中配置微服务能够接受的流量进行放行,避免服务压力过大。
常见网关的实现
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的使用
创建模块(项目)
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<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>
添加启动类
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
org.springframework.boot.SpringApplication.run(GatewayApplication.class, args);
}
}
添加配置
server:
port: 8370
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 网关相关配置
gateway:
routes:
- id: order-service # 路由规则id,随便起
uri: lb://order-service # 访问的路径 这里的order-service和order-service的application.yml中的name要一致
predicates:
- Path=/order/** # 满足这个条件,才会转发到上面的uri
id: 自定义路由ID,保持唯一。
uri: 目标服务地址,支持普通URI及lb://应用注册服务名称。lb表示负载均衡,使用lb://方式表示从注册中心获取服务地址。
predicates: 路由条件,根据匹配结果决定是否执行该请求路由。上述代码中,我们把符合Path规则的一切请求,都代理到uri参数指定的地址。
启动测试
没有配置的就访问不了
在配置文件中添加,重启查看效果
# 网关相关配置
gateway:
routes:
- id: order-service # 路由规则id,随便起
uri: lb://order-service # 访问的路径 这里的order-service和order-service的application.yml中的name要一致
predicates:
- Path=/order/**, /feign/** # 满足这个条件,才会转发到上面的uri
上面只测试了order-service,现在把product-service的加入
# 网关相关配置
gateway:
routes:
- id: order-service # 路由规则id,随便起
uri: lb://order-service # 访问的路径 这里的order-service和order-service的application.yml中的name要一致
predicates:
- Path=/order/**, /feign/** # 满足这个条件,才会转发到上面的uri
- id: product-service
uri: lb://product-service
predicates:
- Path=/product/**
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可以通过 与 逻辑进行组合。
底层原理
使用的是Predicate,是Java 8提供的一个函数式编程接口,它接收一个参数并返回一个布尔值,用于条件过滤和请求参数的校验。
使用文档
官方使用文档:Route Predicate Factories :: Spring Cloud Gateway
这些条件如果写在同一个id下,那它们之间是 与 的关系,必须同时满足条件才能访问。
# 网关相关配置
gateway:
routes:
- id: order-service # 路由规则id,随便起
uri: lb://order-service # 访问的路径 这里的order-service和order-service的application.yml中的name要一致
predicates:
- Path=/order/**, /feign/** # 满足这个条件,才会转发到上面的uri
- id: product-service
uri: lb://product-service
predicates: # 只有同时满足path和after两个条件,才会转发到上面的uri
- Path=/product/**
- After=2024-06-24T15:53:22.370856700+08:00[Asia/Shanghai]
Gateway Filter Factories
Gateway Filter Factories(网关过滤器工厂) 是用来定义网关过滤器的工厂类,用于在请求处理前后添加逻辑。在Spring Cloud Gateway中,网关过滤器可以分为两种类型:Pre类型和Post类型。
-
Pre类型过滤器:在路由处理之前执行,也就是在请求转发到后端服务之前执行。Pre类型过滤器通常用于做鉴权、限流等操作,可以在请求进入后端服务之前对请求进行处理和验证。
-
Post类型过滤器:在请求执行完成后,将结果返回给客户端之前执行。Post类型过滤器主要用于对响应结果进行格式化或者日志记录等操作,可以在响应返回客户端之前对响应进行最后的处理。
这些过滤器可以通过Gateway Filter Factories来定义和配置,以实现特定的请求处理逻辑和流程控制,使得网关在转发请求时能够灵活地应用各种预处理和后处理操作。
Spring Cloud Gateway中内置了许多Filter,用于拦截和链式处理web请求,例如权限校验和访问超时设定。从作用范围上来看,Spring Cloud Gateway将Filter分为两种类型:GatewayFilter和GlobalFilter。
-
GatewayFilter:应用到单个路由或者一个分组的路由上。可以针对特定的路由或路由分组进行过滤器的配置,使得针对某些特定请求可以添加特定的过滤逻辑。
-
GlobalFilter:应用到所有的路由上,也就是对所有的请求都生效。全局过滤器可以用于实现一些通用的逻辑,例如统一的日志记录、全局的异常处理等,对所有的请求都会生效。
通过这两种Filter的组合和配置,Spring Cloud Gateway提供了强大的功能来处理请求的前置和后置逻辑以及全局的请求处理,使得网关能够灵活地应用各种过滤器来满足不同的需求。
GatewayFilter
GatewayFilter同Predicate类似,都是在配置文件application.yml中配置,每个过滤器的逻辑都是固定的。例如,AddRequestParameterGatewayFilterFactory只需要在配置文件中写AddRequestParameter,就可以为所有的请求添加一个参数。
使用
下面的filters配置:
# 网关相关配置
gateway:
routes:
- id: order-service # 路由规则id,随便起
uri: lb://order-service # 访问的路径 这里的order-service和order-service的application.yml中的name要一致
predicates:
- Path=/order/**, /feign/** # 满足这个条件,才会转发到上面的uri
- id: product-service
uri: lb://product-service
predicates: # 只有同时满足path和after两个条件,才会转发到上面的uri
- Path=/product/**
- After=2024-06-24T15:53:22.370856700+08:00[Asia/Shanghai]
filters: # 添加请求参数 当访问上面的uri时,会自动添加一个参数name,值为zhangsan
- AddRequestParameter=name,zhangsan
该 filter 只添加在了 product-service 路由下,因此只对 product-service 路由生效,也就是说对 /product/** 的请求生效。
/**
* product-service中的PorductController
* @param id
* @param name name来自filter
* @return
*/
@RequestMapping("/p1")
public String p1(Integer id, String name) {
log.info("p1接收到 filter 中的 name 参数:" + name);
return "product-service 接收到参数, id:" + id;
}
上面是一般的filter设置,它只会对某个路由生效,但是还有一个default-filter,它可以对所有的路由生效。
注意default-filters的层级位置
# 网关相关配置
gateway:
default-filters: # 默认的过滤器,所有路由都会经过这些过滤器
- name: Retry # 重试 当相应状态是BAD_GATEWAY,会重试3次
args:
reties: 3
status: BAD_GATEWAY
routes:
- id: order-service # 路由规则id,随便起
uri: lb://order-service # 访问的路径 这里的order-service和order-service的application.yml中的name要一致
predicates:
- Path=/order/**, /feign/** # 满足这个条件,才会转发到上面的uri
- id: product-service
uri: lb://product-service
predicates: # 只有同时满足path和after两个条件,才会转发到上面的uri
- Path=/product/**
- After=2024-06-24T15:53:22.370856700+08:00[Asia/Shanghai]
filters: # 添加请求参数 当访问上面的uri时,会自动添加一个参数name,值为zhangsan
- AddRequestParameter=name, zhangsan
/**
* order-service下的TestFeignController
* @param id
* @param response 把response的状态码设置成502,测试一下default-filters配置
* @return
*/
@RequestMapping("/o1")
public String o1(Integer id, HttpServletResponse response){
response.setStatus(502);
return productApi.p1(id);
}
结果如下:
一次正常的访问,三次是重试的。并且order-service没有配置filter参数name,所以也收不到。
文档
官方文档:GatewayFilter Factories :: Spring Cloud Gateway
GlobalFilter
GlobalFilter 是 Spring Cloud Gateway 中的全局过滤器
它和 GatewayFilter 的作用相同。GlobalFilter 会应用到所有的路由请求上,全局过滤器通常用于实现与安全性、性能监控和日志记录等相关的全局功能。
Spring Cloud Gateway 内置的全局过滤器也有很多,例如:
- Gateway Metrics Filter: 网关指标,提供监控指标。
- Forward Routing Filter: 用于本地 forward,请求不转发到下游服务器。
- LoadBalancer Client Filter: 针对下游服务,实现负载均衡。
这些内置的全局过滤器帮助开发者轻松地实现常见的网关功能,无需手动编写复杂的逻辑。
使用
用监控信息举例
引入依赖
在gateway模块中pom中添加以下依赖。
<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: always # 总是显示健康检查的详细信息
shutdown:
enabled: true # 启用关闭管理端点
显示所有监控的信息链接:访问127.0.0.1:8370/actuator,也能点进去具体看看
文档
官方文档:Global Filters :: Spring Cloud Gateway
过滤器执行顺序
在一个项目中,当同时存在 GatewayFilter 和 GlobalFilter 时,它们的执行顺序是根据它们各自指定的 order 值来决定的,具体如下:
- 合并过滤器链: 当请求经过路由后,Spring Cloud Gateway 会将当前项目中的所有 GatewayFilter 和 GlobalFilter 合并到一个过滤器链中。
- 排序规则:每个过滤器都必须指定一个 int 类型的 order 值,用来表示其执行优先级。order 值越小,优先级越高,执行顺序越靠前。
- 优先级排序:过滤器按照 order 值从小到大排序执行。当多个过滤器的 order 值相同时,按照以下顺序依次执行:默认的内置过滤器 (defaultFilter) GatewayFilter GlobalFilter
指定 order 值的方法:
- 实现 org.springframework.core.Ordered 接口,并实现 getOrder() 方法返回 order 值。
- 使用 @Order 注解标注在过滤器类上,指定 order 值。
自定义过滤器的 order 值:对于由用户自定义的过滤器,可以通过上述两种方法之一来指定 order 值,以便控制其在过滤器链中的执行顺序。
Spring Cloud Gateway 会根据每个过滤器的 order 值进行排序,然后依次执行。当 order 值相同时,按照默认过滤器 > GatewayFilter > GlobalFilter 的顺序执行。
自定义过滤器
Spring Cloud Gateway 提供了过滤器的扩展功能,开发者可以根据实际业务需求自定义过滤器。自定义过滤器也支持 GatewayFilter 和 GlobalFilter 两种类型。
自定义GatewayFilter
自定义 GatewayFilter 需要实现对应的接口 GatewayFilterFactory。Spring Boot 默认提供了一个实现该接口的抽象类 AbstractGatewayFilterFactory,我们可以直接使用它。
配置
package com.demo.gateway;
import lombok.Data;
@Data
public class CustomConfig {
private String name;
}
实现
package com.demo.gateway;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomConfig> implements Ordered {
public CustomGatewayFilterFactory() {
super(CustomConfig.class);
}
@Override
public GatewayFilter apply(CustomConfig config) {
return new GatewayFilter() {
/**
* ServerWebExchange: HTTP 请求-响应交互契约, 提供了对HTTP请求和响应的访问
* GatewayFilterChain: 过滤器链
* Mono: Reactor的核心类, 数据流发布者,Mono最多只能触发一个事件.可以把Mono用在异步完成任务时,发出通知
* chain.filter(exchange) 执行请求
* Mono.fromRunnable() 创建一个包含Runnable元素的数据流
*/
@Override
public 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类型过滤器代码逻辑
}));
}
};
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
类名统一以 GatewayFilterFactory 结尾,因为默认情况下,过滤器的 name 会采用该定义类的前缀。这里的 name=Custom(在 yml 配置中使用)。
apply 方法中,同时包含 Pre 和 Post 过滤,then 方法中是请求执行结束之后处理的。
CustomConfig 是一个配置类,该类只有一个属性 name,和 yml 的配置对应。
该类需要交给 Spring 管理,所以需要加 @Component 注解。
getOrder 表示该过滤器的优先级,值越大,优先级越低。
过滤器配置
最下面就是自定义的过滤器。
spring:
application:
name: gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
# 网关相关配置
gateway:
metrics:
enabled: true
default-filters: # 默认的过滤器,所有路由都会经过这些过滤器
- name: Retry # 重试 当相应状态是BAD_GATEWAY,会重试3次
args:
reties: 3
status: BAD_GATEWAY
routes:
- id: order-service # 路由规则id,随便起
uri: lb://order-service # 访问的路径 这里的order-service和order-service的application.yml中的name要一致
predicates:
- Path=/order/**, /feign/** # 满足这个条件,才会转发到上面的uri
filters: # 这是自定义的过滤器
- name: Custom #过滤器名称
args:
name: test_custom
自定义GlobalFilter
实现一个 GlobalFilter 是相对简单的,它不需要额外的配置,只需实现 GlobalFilter 接口即可。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.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("Pre Global Filter");
return chain.filter(exchange).then(Mono.fromRunnable(()->{
log.info("Post Global Filter...");
}));
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}