网关介绍
在微服务架构体系中,一个系统会被拆分为很多个微服务,那么作为客户端要如何去调用这么多的微服务呢?如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别调用,当然这样是不现实的
现有网关介绍
Kong
基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用,只支持http调用
Zuul
Netflix开源的网关,功能丰富,使用JAVA开发,易于二次开发,性能不如nginx
Spring Cloud Gateway
Spring公司为了替换Zuul而开发的网关服务,spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor ,webFlux,底层基于Netty等技术开发的网关,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能
前置知识请参考本专栏webFlux 或者传送门
网关的作用
1、能针对客户端提供统一入口(类似于守门员、门卫)
2、实现统一鉴权
3、实现流量控制
4、实现服务熔断降级
Spring Cloud Gateway的核心概念
路由Route
是 gateway 中最基本的组件之一,路由是网关的基本单元,由ID、URI、一组Predicate、一组Filter组成,根据Predicate进行匹配转发,具体包含如下信息:
id:路由标识符,区别于其他 Route。
uri:路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
order:用于多个 路由之间的排序,数值越小排序越靠前,匹配优先级越高。
predicate:断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由。
filter:过滤器用于修改请求和响应信息。
列举一下配置:
routes:
# 路由的名字
- id: cloud-test-service
# lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
uri: lb://cloud-test-service
# 断言符合这个规定的才进行1转发
predicates:
- Path=/common/**
filters:
# 将第一层去掉
- StripPrefix=1
# 这里使用内置的过滤器,修改返回状态
- SetStatus=2000
转发规则
Predicate(谓语、断言) | 路由转发的判断条件,目前SpringCloud Gateway支持多种方式,常见如:Path、Query、Method、Header等,写法必须遵循 key=vlue的形式 |
Filter(过滤器) | 过滤器是路由转发请求时所经过的过滤逻辑,可用于修改请求、响应内容 |
Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,接下来我们接下 Spring Cloud GateWay 内置几种 Predicate 的使用
规则 | 实例 | 说明 |
Path | - Path=/gate/,/rule/ | 当请求的路径为gate、rule开头的时,进行转发 |
Before | - Before=2017-01-20T17:42:47.789-07:00[America/Denver] | 在某个时间之前的请求才会被转发 |
After | - After=2017-01-20T17:42:47.789-07:00[America/Denver] | 在某个时间之后的请求才会被转发 |
Between | - Between=2017-01-20T17:42:47.789-07:00[America/Denver],2017-01-21T17:42:47.789-07:00[America/Denver] | 在某个时间段之间的才会被转发 |
Cookie | - Cookie=chocolate, ch.p | 名为chocolate的表单或者满足正则ch.p的表单才会被匹配到进行请求转发 |
Header | - Header=X-Request-Id, \d+ | 携带参数X-Request-Id或者满足\d+的请求头才会匹配 |
Host | - Host=www.sltech.com | 当主机名为www.sltech.com的时候,转发 |
Method | - Method=GET | 只有GET方法才会匹配转发请求,还可以限定POST、PUT等请求方式 |
server:
port: 8080
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=smile
这样配置,只要请求中包含 smile 属性的参数即可匹配路由
server:
port: 8080
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
- id: gateway-service
uri: https://www.baidu.com
order: 0
predicates:
- Host=**.foo.org
- Path=/headers
- Method=GET
- Header=X-Request-Id, \d+
- Query=foo, ba.
- Query=baz
- Cookie=chocolate, ch.p
路由断言规则可以组合使用
过滤器
过滤器规则
过滤规则 | 实例 | 说明 |
PrefixPath | - PrefixPath=/app | 在请求路径前加上app |
RewritePath | - RewritePath=/test, /app/test | 访问localhost:10010/test,请求会转发到localhost:10010/app/test |
SetPath | SetPath=/app/{path} | 通过模板设置路径,转发的规则时会在路径前增加app,{path}表示原请求路径 |
RedirectTo | - RedirectTo=303, https://acme.org | 重定向定义返回码和地址 |
RemoveRequestHeader | - RemoveRequestHeader=X-Request-Foo | 去掉请求头信息 X-Request-Foo |
举一个例子,不一一列举 :
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: https://example.org
filters:
- RemoveResponseHeader=X-Request-Foo
PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择 请求的微服务、记录调试信息等
POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等
Gateway 的Filter从作用范围可分为两种: GatewayFilter与GlobalFilter:
- GatewayFilter:应用到单个路由或者一个分组的路由上。
- GlobalFilter:应用到所有的路由上
过滤器生效顺序
GlobalFilter 的功能其实和 GatewayFilter 是相同的,只是 GlobalFilter 的作用域是所有的路由配置,而不是绑定在指定的路由配置上。多个 GlobalFilter 可以通过 @Order 或者 getOrder() 方法指定执行顺序,order值越小,执行的优先级越高。
由于过滤器分为 pre 和 post 两种类型,pre 类型过滤器如果 order 值越小,那么它就应该在pre过滤器链的顶层,post 类型过滤器如果 order 值越小,那么它就应该在 post 过滤器链的底层:
Spring Cloud Gateway的搭建及过滤器实现
项目搭建
新建springBoot项目,版本2.3.5.RELEASE,我这里cloud版本是Hoxton.SR12
<dependencies>
<!-- 网关依赖-->
<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>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
添加配置
server:
port: 10010
spring:
application:
name: could-gateway-server
# 必须加这句话,让gateway可以发现注册中心的服务
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
# 路由的名字
- id: cloud-test-service
# lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
uri: lb://cloud-test-service
# 断言符合这个规定的才进行1转发
predicates:
- Path=/common/**
filters:
# 将第一层去掉
- StripPrefix=1
# 这里使用内置的过滤器,修改返回状态
- SetStatus=2000
nacos:
discovery:
server-addr: 127.0.0.1:8848
添加过滤器
局部过滤器
局部过滤器需要继承AbstractGatewayFilterFactory,重写apply方法,过滤器名称必须叫xxxGatewayFilterFactory,配置的路由的时候可以直接配置xxx
import lombok.Data;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Component
public class myParamGateWayFilterFactory extends AbstractGatewayFilterFactory<MyCustomLocalFiterFactory.ParamsConfig> {
@Override
public String name() {
return "mycustomlocalfiter";
}
public MyCustomLocalFiterFactory() {
super(ParamsConfig.class);
}
@Override
public GatewayFilter apply(ParamsConfig config) {
return (exchange, chain) -> {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
System.out.println("name=" + config.getParamName() + ";value=" + config.getParamValue());
}));
};
}
@Data
public static class ParamsConfig {
private String paramName;
private String paramValue;
}
}
应用到路由上
filters:
- StripPrefix=1
- name: myParam
args:
paramname: "this is paramName"
paramvalue: "this is paramValue"
全局过滤器
全局过滤器交给spring容器管理,无需任何配置即可生效,全局过滤器针对所有路由有效,
注意:写在then之中的是后置逻辑,见下方示例:
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
log.info("BFilter前置逻辑");
return chain.filter(exchange).then(Mono.fromRunnable(() ->
{
log.info("BFilter后置逻辑");
}));
}
前置过滤器
@Component
public class MyCustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("this is custom pre filter");
return chain.filter(exchange).then();
}
@Override
public int getOrder() {
return 0;
}
}
后置过滤器
@Component
public class MyCustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 写在then中
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
System.out.println("this is post filter");
}));
}
@Override
public int getOrder() {
return 0;
}
}