网关处理流程图
1.Gateway--Pom文件
<?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">
<parent>
<artifactId>01-sca</artifactId>
<groupId>com.jt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sca-gateway</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--添加gateway依赖:假如依赖下载不下来:
1)保证网络没有问题(手机网络,公司网络)
2)保证maven配置正确(maven软件,远程仓库)
3)本地库(本地库中尽量不要存在同一个资源的多个版本)
-->
<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>
<!--拓展:基于如下依赖中的API可以实现对象与json字符串之间转换-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
2.核心配置文件
server:
port: 9000
spring:
application:
name: sca-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8180
#port: 8719
eager: true #服务启动时会会向sentinel控制台发送心跳,为我们的应用创建菜单
gateway: # API Gateway (负责API管理)
discovery:
locator:
enabled: true #开启基于服务名获取服务实例的功能(基于服务名创建路由)
routes: #网关路由配置
- id: router01 #http://localhost:9000/nacos/provider/echo/hello
#uri: http://localhost:8081
uri: lb://sca-provider #lb表示负载均衡,底层默认使用ribbon实现
predicates: #定义请求规则(请求需要按照此规则设计)
- Path=/nacos/provider/echo/** #请求路径设计
#- After=2021-08-23T13:59:59.789+08:00[Asia/Shanghai]
#- Header=X-Request-Id, \d+ #\d+表示任何一个数字
#- Method=GET
#- Query=pageSize,\d+
filters:
- StripPrefix=1 #转发之前去掉path中第一层路径,例如nacos
#- AddRequestHeader=X-Request-Foo, Bar
#- AddRequestParameter=foo, hello
#自己定义白名单路径
white:
prefix: /nacos
# 假如希望调整负载均衡算法,可参考如下配置方式
sca-provider: #这个名字为指定的服务名
ribbon:
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
- id,路由标识符,区别于其他 Route。
- uri,路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
- predicate,断言(谓词)的作用是进行条件判断,只有断言都返回真,才会执行路由。
- filter,过滤器用于修改请求和响应信息。
3.断言(Predicate)分析
3.1请求时间判断
此类型的断言根据时间做判断,主要有三个:
1) AfterRoutePredicateFactory:判断请求日期是否晚于指定日期
2) BeforeRoutePredicateFactory:判断请求日期是否早于指定日期
3) BetweenRoutePredicateFactory:判断请求日期是否在指定时间段内
-After=2020-12-31T23:59:59.789+08:00[Asia/Shanghai]
当且仅当请求时的时间After配置的时间时,才转发该请求,若请求时的时间不是After配置的时间时,则会返回404 not found。时间值可通过ZonedDateTime.now()获取.
3.2请求头判断
判断请求Header是否具有给定名称且值与正则表达式匹配。例如:
-Header=X-Request-Id, \d+
3.3请求方式判断
MethodRoutePredicateFactory接收一个参数,判断请求类型是否跟指定的类型匹配。例如:
-Method=GET
3.4请求参数判断
接收两个参数,请求param和正则表达式, 判断请求参数是否具 有给定名称且值与正则表达式匹配。例如:
-Query=pageSize,\d+
4.过滤器(Filter)增强分析
过滤器(Filter)就是在请求传递过程中,对请求和响应做一个处理。Gateway 的Filter从作用范围可分为两种:GatewayFilter与GlobalFilter。其中:
GatewayFilter:应用到单个路由或者一个分组的路由上。
GlobalFilter:应用到所有的路由上
局部过滤器实现(配置文件中的filters)
基于AddRequestHeaderGatewayFilterFactory,为原始请求添加Header。
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-Foo, Bar
基于AddRequestParameterGatewayFilterFactory,为原始请求添加请求参数及值,
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
filters:
- AddRequestParameter=foo, bar
基于PrefixPathGatewayFilterFactory,为原始的请求路径添加一个前缀路径
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- PrefixPath=/mypath
基于RequestSizeGatewayFilterFactory,设置允许接收最大请求包的大小
spring:
cloud:
gateway:
routes:
- id: request_size_route
uri: http://localhost:8080/upload
predicates:
- Path=/upload
filters:
- name: RequestSize
args:
# 单位为字节
maxSize: 5000000
全局过滤器设计及实现
设置访问的黑白名单:
package com.jt.config;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
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;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义的全局过滤器,作用域所有路由
*/
@Component
public class AuthGatewayFilter implements GlobalFilter, Ordered {
@Value("${white.prefix}")
private String whitePrefix;
/**你的业务可以写到这个filter方法内部
* @param exchange 基于此对象可以获取请求和响应对象
* @param chain 这个对象指向了一个过滤链(这个链中有多个过滤器)
* */
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
//1.获取请求对象
ServerHttpRequest request = exchange.getRequest();
//2.获取请求数据
String path = request.getURI().getPath();
System.out.println("path="+path);
//String username=request.getQueryParams().getFirst("username");
//3.对请求数据进行处理
//例如判定请求的path是否在我们允许的范围之内
System.out.println("whitePrefix="+whitePrefix);
if(!path.startsWith(whitePrefix)) {//判断路径的前缀
//获取响应对象
ServerHttpResponse response = exchange.getResponse();
//设置响应状态码,这里的值为502
//response.setStatusCode(HttpStatus.BAD_GATEWAY);
//结束当前请求
//return response.setComplete();//直接结束
//假如希望响应一些具体内容到客户端
//String content="request failure";
//byte[] bytes=content.getBytes();
Map<String,Object> map=new HashMap<>();
map.put("message","request failure");
map.put("status",502);
//将map对象转换为json字符串?
//String jsonStr="{\"message\":\"request failure\",\"status\":502}";
Gson gson=new Gson();
String jsonStr=gson.toJson(map);//将map对象转换json格式字符串.
byte[] bytes=jsonStr.getBytes();
DataBuffer dataBuffer=
response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(dataBuffer));
//Spring WebFlux,Mono是一个可以封装0个或1个元素的序列对象.
}
//4.返回响应结果
return chain.filter(exchange);//将请求交给下一个过滤器进行处理
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
5.限流设计及实现
网关是所有请求的公共入口,所以可以在网关进行限流,而且限流的方式也很多,我们采用Sentinel组件来实现网关的限流。Sentinel支持对SpringCloud Gateway、Zuul等主流网关进行限流。参考网址如下:
https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel
启动网关项目,检测sentinel控制台的网关菜单。
启动时,添加sentinel的jvm参数,通过此菜单可以让网关服务在sentinel控制台显示不一样的菜单,代码如下。
-Dcsp.sentinel.app.type=1
基于请求属性限流
基于请求属性限流
自定义API维度限流(重点)
新建api分组
新建分组流控规则,如图所示:
定制流控网关返回值
package com.jt.config;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class GatewayConfig {
public GatewayConfig(){
GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
ServerHttpResponse response = serverWebExchange.getResponse();
Map<String,Object> map=new HashMap<>();
map.put("state",429);
map.put("message","two many request");
//String jsonStr= JSON.toJSONString(map);
Gson gson = new Gson();
String s = gson.toJson(map);
return ServerResponse.ok().body(Mono.just(s), String.class);
}
});
}
}