一、网关
在微服务框架中,网关是一个提供统一访问地址的组件,它充当了客户端和内部微服务之间的中介。网关主要负责流量路由和转发,将外部请求引导到相应的微服务实例上,同时提供一些功能,如身份认证、授权、限流、监控、日志记录等。
二、网关作用
- **路由功能:**网关可以根据目标地址的不同,选择最佳的路径将数据包从源网络路由到目标网络。它通过维护路由表来确定数据包的转发方向,并选择最优的路径。
- **安全控制:**网关可以实施网络安全策略,对进出的数据包进行检查和过滤。它可以验证和授权来自源网络的数据包,并阻止未经授权的访问。防火墙是一种常见的网关设备,用于过滤和保护网络免受恶意攻击和未经授权的访问。
- **协议转换:**不同网络使用不同的通信协议,网关可以进行协议转换,使得不同网络的设备可以互相通信。例如:将HTTPS 协议转换成 HTTP 协议。
- **网络地址转换(NAT):**网关可以执行网络地址转换,将内部网络使用的私有 IP 地址转换为外部网络使用的公共 IP 地址,以实现多台计算机共享一个公共 IP 地址上网
三、Spring Cloud Gateway 组成
- **路由:**定义了请求应该被转发到哪个目标地址。路由由 ID、目标 URI 、断言、和过滤器组成。通过配置多个路由,可以实现不同请求的路由规则。
- **断言:**用于匹配请求条件,如果请求匹配断言条件,则会被映射到对应目标地址上。断言可以基于请求的路径、请求头、请求参数等信息进行匹配。
- **过滤器:**用于在请求路由前或路由后进行一些处理,如添加头部信息、修改请求体等。过滤器可以在全局范围或特定路由范围内配置,多个路由器可以组成过滤器链。
四、Spring Cloud Gateway 的使用
使用步骤如下:
1. 添加 Gateway 依赖
XML
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2. 设置网关路由规则
bash
spring:
cloud:
gateway:
routes:
- id: user-service
uri: http://192.168.146.1:8082 #映射的ip和端口
predicates:
- Path=/user/**,/log/** #当请求路径满足配置的path时,将ip和端口映射到上面的uri
五、断言(Predicate)类型
Spring Cloud Gateway 支持的断言类型目前有 12 中, 包含以下这些:
- 根据时间匹配:
- After: 请求在指定时间之后才匹配
- Before:请求在指定时间之前才匹配
- Between:请求在指定时间中间才匹配
-
Cookie:匹配求中的Cookie值
-
Header:匹配请求中的 Header 值
-
Host:匹配请求中的 Host 值
-
Method:匹配请求头中的 Method 的值
-
Path:匹配请求路径
-
Query:匹配请求参数
-
RemoteAddr:匹配请求的 IP 地址,支持 IPV4 和 IPV6
-
Weight:根据权重来分发请求,权重根据 group 来计算
-
XForwardedRemoteAddr:根据 X-Forwarded-For 匹配
5.1 根据时间匹配
bash
spring:
cloud:
gateway:
routes:
- id: user-service
uri: http://192.168.146.1:8082
predicates:
- Path=/user/**,/log/**
- Before=2024-07-15T19:00:00.000+08:00[Asia/Shanghai] #上海时间2024-7-15 19点前允许访问
between
bash
spring:
cloud:
gateway:
routes:
- id: user-service
uri: http://192.168.146.1:8082
predicates:
- Path=/user/**,/log/**
- Between=2024-07-13T19:00:00.000+08:00[Asia/Shanghai],2024-07-15T19:00:00.000+08:00[Asia/Shanghai] #上海时间2024-7-13 19点 ~ 2024-7-15 19点之间允许访问
5.2 根据 Header 匹配
bash
spring:
cloud:
gateway:
routes:
- id: user-service
uri: http://192.168.146.1:8082
predicates:
- Path=/user/**,/log/**
- Between=2024-07-13T19:00:00.000+08:00[Asia/Shanghai],2024-07-15T19:00:00.000+08:00[Asia/Shanghai] #上海时间2024-7-15 19点前允许访问
- Header= Ip2contry,\w+
其中 "Ip2contry" 表示 Header 中的 key ,而 "\w+" 表示的是key 的值,值可用正则表达式进行匹配
5.3 根据 Cookie 匹配
bash
spring:
cloud:
gateway:
routes:
- id: user-service
uri: http://192.168.146.1:8082
predicates:
- Path=/user/**,/log/**
- Between=2024-07-13T19:00:00.000+08:00[Asia/Shanghai],2024-07-15T19:00:00.000+08:00[Asia/Shanghai] #上海时间2024-7-15 19点前允许访问
- Header= Ip2contry,\w+
- Cookie= author,zhangsan
5.4 根据 weight 进行配置
bash
spring:
cloud:
gateway:
routes:
- id: user-service
uri: http://169.254.165.28:8081
predicates:
- Weight=group2,10
- id: user-service2
uri: http://169.254.165.28:8082
predicates:
- Weight=group2,90
group2 表示分组,10 表示所在分组所占权重
六、Spring Cloud Gateway + Nacos + LoadBalancer 实现企业级网关
6.1 添加依赖
XML
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</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-gateway</artifactId>
</dependency>
6.2 设置路由规则
bash
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service #user-service #调用服务注册在 nacos 中的名称
predicates:
- Path=/user/**
nacos:
discovery:
server-addr: localhost:8848
password: nacos
username: nacos
register-enabled: false
七、过滤器
过滤器作用:
- **功能扩展和定制:**过滤器可以用于对现有功能进行扩展和定制。通过拦截和处理数据流或事件流,可以修改数据,增加额外的功能逻辑,实现特定的业务需求。例如,可以使用过滤器在请求之前进行身份验证、权限控制,或者在响应之后进行日志记录、数据转换等操作。
- **数据校验和过滤:**过滤器可以用于对数据进行校验和过滤。在接收到数据之后,可以使用过滤器对数据进行检查,验证数据的合法性,过滤掉无效或不符合要求的数据。这有助于保证数据的准确性、完整性和安全性。
- **安全保护:**过滤器可以用于提供安全保护措施,可以使用过滤器对输入的请求进行检查和清洗,以防止潜在的安全漏洞,例如跨站脚本攻击(XSS)、跨站请求伪造(CSRF) 等攻击。过滤器还可以进行访问控制,验证权限和实施安全策略。
- 性能优化:过滤器可以用于性能优化。例如,在数据处理流水线中,可以使用过滤器对数据进行转换、过滤或缓存,以提高处理速度和效率。过滤器还可以用于数据压缩、缓存预热等场景,减少数据传输和处理的成本。
- 统一处理和逻辑服用:过滤器提供了一种统一的处理方式,可以在不同的组件或模块上应用相同的逻辑或处理方式。通过将处理逻辑抽象为过滤器,可以避免重复代码、统一错误处理和统一异常处理等,提高代码复用性和可维护性。
7.1 内置局部过滤器
bash
spring:
cloud:
gateway:
routes:
- id: user-service-demo
uri: lb://user-service
predicates:
- Path=/user/**
filters:
- AddResponseHeader=soruce,userservice
其中 "AddResponseHeader" 表示添加返回头过滤器,前面的值 " source" 为 key ,后面的值 "value" 为 value
7.2 AddRequestHeader
bash
spring:
cloud:
gateway:
routes:
- id: user-service-demo
uri: lb://user-service
predicates:
- Path=/user/**
filters:
- AddResponseHeader=soruce,userservice
- AddRequestHeader= my-request-market, kaikai
在下游服务调用接口中打印所有的 Header 查看添加的 Header 信息,代码如下:
java
@RequestMapping("/getHeader")
public void getHeader(HttpServletRequest request){
Enumeration<String> headers= request.getHeaderNames();
while (headers.hasMoreElements()){
String key=headers.nextElement();
String value= request.getHeader(key);
System.out.println(key+ " : "+value);
}
}
7.3 AddRequestParameter
bash
spring:
cloud:
gateway:
routes:
- id: user-service-demo
uri: lb://user-service
predicates:
- Path=/user/**
filters:
- AddRequestParameter= id,209
后续请求中带有key为id,value 209 的参数
java
@RequestMapping("/getID")
public Integer getID(@RequestParam("id")Integer id){
return id;
}
@RequestMapping("/getId")
public String getId(HttpServletRequest request){
return request.getParameter("id");
}
7.4 PrefixPath
在请求的 url 前面添加前缀,例如请求的是 /user, 添加了 PrefixPath为 /new,那么后续访问的地址就为 "/new/user"
7.5 限流过滤器- RequestRateLimiter
网关限流过滤器,Spring Cloud Gateway 内置了限流功能,它使用的限流算法是令牌桶的限流算法
**令牌桶限流算法:**令牌安固定的速率被放入令牌桶中,桶中最多存放 N个令牌(Token),当桶装满时,新添加的令牌被丢弃或拒绝。当请求到达时,将从桶中删除 1 个令牌。令牌中的令牌不仅可以被移除,还可以往里添加,所以为了保证接口随时有数据通过,必须不停地往桶里加令牌。由此可见,往桶里加令牌的速度就决定了数据通过接口的速度。我们通过控制往令牌桶里加令牌的速度从而控制接口的流量。
Spring Cloud Gateway 当前版本支持 和 Redis 一起实现限流功能,它的实现步骤总共分为以下三步:
1. 添加 Reids 框架依赖
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
2. 创建限流规则
创建一个类,根据 IP 进行限流:
java
@Component
public class IpKeyResolver implements KeyResolver {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
System.out.println(exchange.getRequest().getRemoteAddress().getHostString());
System.out.println(exchange.getRequest().getRemoteAddress().getHostName());
return Mono.just(exchange.getRequest().getRemoteAddress().getHostString());
}
}
3. 配置限流规则
在项目的配置文件中,配置以下内容:
bash
spring:
cloud:
gateway:
routes:
- id: user-service-demo
uri: lb://user-service
predicates:
- Path=/user/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 1
keyResolver: '#{@ipKeyResolver}'
nacos:
discovery:
username: nacos
password: nacos
server-addr: localhost:8848
data:
redis:
host: 114.115.149.19
port: 6378
database: 0
其中,name 必须等于 "RequestRateLimiter" 内置限流过滤器,其他参数:
- redis-rate-limiter.replenishRate:令牌填充速度:每秒允许请求数
- redis-rate-limiter.burstCapacity: 令牌桶容量:最大令牌数
- redis-rate-limiter.requestedTokens: 每次请求消耗的令牌数
- keyResolver: 根据哪个 key 进行限流,它的值时 spEL 表达式。
7.6 Retry
配置:
bash
spring:
cloud:
gateway:
routes:
- id: user-service-demo
uri: lb://user-service
predicates:
- Path=/user/**
filters:
- name: Retry
args:
retries: 3 #重试次数
statuses: Gateway Timeout #状态码,详情参考 HttpResponseStatus.class
method: GET
# series: CLIENT_ERROR
backoff:
firstBackoff: 1000ms #第一次重试间隔
maxBackoff: 10000ms #最大重试间隔时间
factor: 2 #firstBackoff * (factor ^ n) 重试系数,如果重试间隔时间超过最大重试间隔时间,按照最大重试间隔时间来算
basedOnPreviousValue: false #根据上次重试时间加上重试系数来计算
代码调试:
java
@RequestMapping("/getstatus")
public void getStatus(HttpServletResponse response){
System.out.println("==============Do GetStatus Method ===================");
response.setStatus(403);
}
- name :一定要等于 "Retry" ,因为 "Retry" 就是内置的过滤器的名字
- retries: 重试次数
- statuses:状态码 ,匹配对应状态码响应,并重试
- series: 状态码配置,符合某段状态码才会进行重试逻辑,默认值是SERVER_ERROR,值是 5 ,也就是 5XX 开头的状态码。
1XX:INFORMATIONAL
2XX:SUCCESSFUL
3XX:REDIRECTION
4XX:CLIENT_ERROR
5XX:SERVER_ERROR- backoff:重试指数配置策略,默认关闭
注:statuses 和 retries 是且的关系。
7.7 内置全局过滤器
bash
spring:
cloud:
gateway:
routes:
- id: user-service-demo
uri: lb://user-service
predicates:
- Path=/user/**
filters:
# - AddResponseHeader=soruce,userservice
default-filters:
- AddResponseHeader=soruce,userservice
7.8 自定义过滤器
java
package com.gatewayservicesoa.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class AuthFilter implements GlobalFilter, Ordered {
/**
* @param exchange 要执行的事件
* @param chain 过滤器链
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//此处使用的是相应试 HTTP
ServerHttpRequest request= exchange.getRequest();
ServerHttpResponse response= exchange.getResponse();
if(request.getQueryParams().getFirst("name")!=null && request.getQueryParams().getFirst("name").equals("name")
&& request.getQueryParams().getFirst("password")!=null && request.getQueryParams().getFirst("password").equals("123")){
return chain.filter(exchange);
}else {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
}
@Override
public int getOrder() {
//过滤器的执行顺序,值越小,越早执行
return 0;
}
}
八、工作原理
客户但向 Spring Cloud Gateway 发出请求,网关映射处理程序(Gateway Handler Mapping) 会根据请求路径进行路由匹配,然后再将其发送到网关 Web 处理器(Gateway Web Handler) 进行处理。此时处理器会经过过滤链(filter) 进行处理,而过滤器又分为 前置过滤器和后置过滤器,之后才会将请求发送给目标服务(也称代理的服务)。