使用服务网关作为接口服务的统⼀代理,前端通过网关完成服务的统⼀调用
Nginx通常被用作应用服务器网关(Tomcat集群,Redis集群,前端浏览器网关),
zuul或者gateway通常被用作服务网关(springcloud微服务)
Route: 路由是网关的基本组成部分,它是由id、目标uri、断⾔组成,如果断⾔为true,则匹配该路由,转向到当前路由的URI
Predicate:断⾔,用户请求的匹配规则
Filter:过滤器,用于对请求进行前置、后置处理(可以在网关实现对请求或相应的加工处理)
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
server:
port: 9999
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
# 配置api-service1路由规则
# id随便起
- id: api-service1
uri: http://localhost:8001
# 断言规则:路径里有product的跳转
# http://localhost:9999/product/query —> http://localhost:8001/product/query
predicates:
- Path=/product/**
# 配置api-service2路由规则
- id: api-service2
uri: http://localhost:8002
predicates:
- Path=/order/**
SpringCloud Gateway提供了多种断⾔匹配的⽅式:
After
Before
Between
Cookie
Header
Host
Method
Path
Query
RemoteAddr
Weight
根据请求路径的正则匹配
server:
port: 9999
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
# 配置api-service1路由规则
# id随便起
- id: api-service1
uri: http://localhost:8001
# 断言规则:路径里有product的跳转
# http://localhost:9999/product/query —> http://localhost:8001/product/query
predicates:
- Path=/product/**
# 配置api-service2路由规则
- id: api-service2
uri: http://localhost:8002
predicates:
- Path=/order/**
根据请求携带的参数匹配路由
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: aaa
uri: http://localhost:8001
predicates:
# http://localhost:9999/product/query?name=wang ---> http://localhost:8001/product/query?name=wang
- Query=name
- id: bbb
uri: http://localhost:8002
predicates:
#如果请求url中带有pwd参数 ---> http://localhost:8002
- Query=pwd
根据Header中携带的参数匹配
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: aaa
uri: http://localhost:8001
predicates:
# 带key为token的请求头
- Header=token
- id: bbb
uri: http://localhost:8002
predicates:
# 带key为aa值为haha的请求头
- Header=aa,haha
根据Host主机名进行匹配转发,如果我们的接口只允许**.yuqiyu.com域名进行访问
spring:
cloud:
gateway:
routes:
- id: blog
uri: http://blog.yuqiyu.com
predicates:
- Host=**.yuqiyu.com
# 或者指定某ip可以访问 http://192.168.155.11:8002
相同的Predicate也可以配置多个,请求的转发是必须满足所有的Predicate后才可以进行路由转发
spring:
cloud:
gateway:
routes:
- id: blog
uri: http://blog.yuqiyu.com
predicates:
- Method=GET
- Host=**.yuqiyu.com
- Path=192.168.1.56/24
gateway网关可以对用户的请求和响应进行处理,gateway提供了多个内置的过滤器,不同的过滤器可以完成不同的请求或者响应的处理
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: aaa
uri: http://localhost:8001
predicates:
- Path=/red/aaa/query/**
filters:
# 对请求添加请求头
- AddRequestHeader=token,wahahaawahaha
# 对请求路径添加请求参数
- AddRequestParameter=username, ergou
# 修改请求状态
- SetStatus=404
# 对url为*/red/*的替换为*/*
# http://localhost:9999/red/product/query —> http://localhost:8001/product/query
- RewritePath=/red(?>/?.*), $\{segment}
# 对匹配断言的url去掉前两个参数(最终和上面的效果相同)
- StripPrefix=2
public class MyFilter01 implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
System.out.println("---------------⾃定义过滤器");
return chain.filter(exchange);
}
//设置此过滤器优先级 0最优先
@Override
public int getOrder() {
return 0;
}
}
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder){
System.out.println("-----------------------init");
RouteLocator routeLocator = builder.routes().route( r->
r.path("/product/**") // predicates
.filters(f->f.filters( new MyFilter01() )) // filters
.uri("http://localhost:8001") //uri
).build();
return routeLocator;
}
}
相当于扩展Gateway内置的网关过滤器
创建⼀个类继承AbstractNameValueGatewayFilterFactory,类名必须以GatewayFilterFactory结尾,类名前⾯的部分即为当前自定义网关过滤器的名字
添加@Component注解,注册到Spring容器
@Component
public class MyFilterGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
System.out.println("name:"+config.getName());
System.out.println("value:"+config.getValue());
//创建⾃定义⽹关过滤器并返回
GatewayFilter gatewayFilter = new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~⾃定义网关过滤器");
return chain.filter(exchange);
}
};
return gatewayFilter;
}
}
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: bbb
uri: http://localhost:8002
predicates:
- Path=/order/**
filters:
- MyFilter=aa,bb
上述过滤器,都是配置在某个路由/服务中,称之为
⽹关服务过滤器
,Gateway提供了内置的全局过滤器
,会拦截过滤所有到达网关服务器的请求。
内置的全局过滤器默认⽣效,⽆需开发者干预。
根据业务的需求我们也可以⾃定义全局过滤器以实现对所有网关请求的拦截和处理(验证token)
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("----------------------------------------------MyGlobalFilter");
List<String> list = exchange.getRequest().getHeaders().get("token");
if (list != null && list.size()>0){
String token = list.get(0);
System.out.println("token:"+token);
return chain.filter(exchange);
}else{
//如果没有token,或者token过期
ServerHttpResponse response = exchange.getResponse();
//设置响应头
response.getHeaders().add("Content-Type","application/json;charset=utf-8");
//设置状态码
response.setStatusCode(HttpStatus.UNAUTHORIZED);
// 封装响应数据
String str = "";
DataBuffer dataBuffer = response.bufferFactory().wrap(str.getBytes());
return response.writeWith(Mono.just(dataBuffer));
}
}
@Override
public int getOrder() {
return 0;
}
}
如果在Gateway网关的路由配置中,直接将服务的ip port配置进去,将导致:
1.如果服务的地址变更,必须要重新配置gateway的路由规则
2.如果服务采⽤集群部署,则不能实现负载均衡
所以Gateway搭配eureka实现动态路由(将Gateway配置中uri改为eureka中服务的名字即可 添加 lb 可以实现负载均衡)
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
server:
port: 9999
spring:
application:
name: gateway-server
main:
web-application-type: reactive
cloud:
gateway:
routes:
- id: aaa
# uri改为eureka中服务消费者的name即可 添加 lb 可以实现负载均衡
uri: lb://api-service1
predicates:
- Path=/product/**
- id: bbb
uri: lb://api-service2
predicates:
- Path=/order/**
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
通过限制用户的请求进⼊到服务中,有效控制应用系统的QPS,达到保护系统的目的
Gateway是基于令牌桶算法,使用redis作为“桶”结合顾虑器实现了网关限流
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redis-reactiveartifactId>
dependency> <dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
<version>2.9.0version>
dependency>
@Configuration
public class AppConfig {
@Bean
public KeyResolver keyResolver() {
//http://localhost:9999/order/query?user=1
//使⽤请求中的user参数的值作为令牌桶的key
//return exchange ->Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
}
}
server:
port: 9999
spring:
application:
name: gateway-server
main:
web-application-type: reactive
cloud:
gateway:
routes:
- id: aaa
uri: lb://api-service1
predicates:
- Path=/product/**
-
- id: bbb
uri: lb://api-service2
predicates:
- Path=/order/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1 #令牌桶每s的填充速度(每秒增加一个可以访问的线程)
redis-rate-limiter.burstCapacity: 2 # 令牌桶容量(最多两个并发访问)
redis-rate-limiter.requestedTokens: 1
key-resolver: "#{@keyResolver}"
redis:
host: 47.96.11.185
port: 7001
password: qfedu123
database: 0
lettuce:
pool:
max-active: 10
max-wait: 1000
max-idle: 5
min-idle: 3
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka