1、API Gateway简单介绍
1.1 什么是API网关?
API网关是所有请求的入口,承载了所有的流量,API Gateway是一个门户一样,也可以说是进入系统的唯一节点。这跟面向对象设计模式中的Facet模式很像。API Gateway封装内部系统的架构,并且提供API给各个客户端。它还可能有其他功能,如授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等
画图表示,没有网关的情况,客户端的请求会直接落到后端的各个服务中,无法集中统一管理
画图表示,有网关的情况,所有的请求都先经过网关,然后进行分发到对应服务
1.2 API网关的作用
网关可以用于授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等,挑几个介绍
- 动态路由
网关可以做路由转发,假如服务信息变了,只要改网关配置既可,所以说网关有动态路由(Dynamic Routing)的作用,如图: - 请求监控
请求监控可以对整个系统的请求进行监控,详细地记录请求响应日志,如图,可以将日志丢到消息队列,如果没有使用网关的话,记录请求信息需要在各个服务中去做 - 认证鉴权
认证鉴权可以对每一个访问请求做认证,拒绝非法请求,保护后端的服务,不需要每个服务都做鉴权,在项目中经常有加上OAuth2.0、JWT,Spring Security进行权限校验 - 压力测试
有网关的系统,如果要要对某个服务进行压力测试,可以如图所示,改下网关配置既可,测试请求路由到测试服务,测试服务会有单独的测试数据库,这样测试的请求就不会影响到正式的服务和数据库
2、SpringCloud Gateway
2.1 What is SpringCloud Gateway?
用公网的解释是:SpringCloud Gateway是一个在Spring生态系统之上构建的API网关,包括:Spring 5,Spring Boot 2,Project Reactor(基于高性能的Reactor模式响应式通信框架Netty,异步阻塞模型)。Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到API,并为它们提供跨领域的关注,例如:安全性,监视/度量和弹性。
SpringCloud Gateway采用了WebFlux,所以使用时不需要引入web场景启动器对应jar,而需要依赖于WebFlux,当然高版本,肯定不需要自己引入,在对应的starter已经集成
2.2 SpringCloud Gateway结构
引用官方图例如图,SpringCloud Gateway的底层基于Netty,主要组成有Predicates(谓词或者断言)、Route(路由)、Filter(过滤器)
画张思维导图表示SpringCloud Gateway的组成:
- 路由(route):网关的基本构建块。它由ID,目标URI,谓词集合和过滤器集合定义
- 过滤器(Filter):这些过滤器是使用特定工厂构造的Spring Framework
GatewayFilter
实例 - 谓词(Predicates): 引用了java8的函数谓词,输入类型是Spring Framework
ServerWebExchange
。谓词可以匹配HTTP请求中的所有内容,例如标头或参数
2.3 SpringCloud Gateway工作方式
从总体上概述了Spring Cloud Gateway的工作方式,引用官网的图例:
从官网的图来看,并不是特别复杂,首先客户端请求都会先经过Gateway Handler Mapping,匹配上就通过Gateway Web Handler转给过滤器处理,过滤器分为PreFilter(前置过滤器)、PostFilter(后置过滤器)。过滤器由虚线分隔的原因是,筛选器可以在发送代理请求之前和之后运行逻辑。所有“前置”过滤器逻辑均被执行。然后发出代理请求。发出代理请求后,将运行“后置”过滤器逻辑
3、Gateway实验环境准备
环境准备:
- JDK 1.8
- SpringBoot2.2.3
- SpringCloud(Hoxton.SR7)
- Maven 3.2+
开发工具
- IntelliJ IDEA
- smartGit
新增SpringBoot Initializer项目:New Module->Spring Initializer,选择jdk版本,至少jdk8
packaging选择jar,java version选择jdk8的,然后点next
选择Gateway的依赖,选择之后会自动加上对应pom配置
Eureka客户端的依赖也可以加上,这样就可以注册服务到eureka服务端
新建项目之后,检查pom是否有spring-cloud-starter-gateway<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
检查pom是否有spring-cloud-starter-netflix-eureka-client
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
如果不通过idea的Spring Initializer新建项目的,需要自己加上:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
本博客的是基于spring-cloud-starter-netflix-eureka-client
进行试验,试验前要运行eureka服务端,eureka服务提供者,代码请参考上一章博客
4、API Gateway简单实现
4.1 YAML配置Eureka和Gateway
Eureka客户端配置:ps,注意在Application加上@EnableEurekaClient
eureka:
client:
# 配置eureka服务地址
service-url:
defaultZone: http://localhost:8761/eureka/
# 关闭eureka健康检查
healthcheck:
enabled: false
# Eureka服务注册开启
register-with-eureka: true
# Eureka服务发现开启
fetch-registry: true
instance:
status-page-url-path: http://localhost:8761/actuator/info
health-check-url-path: http://localhost:8761/actuator//health
# 显示ip
prefer-ip-address: true
# eureka实例id
instance-id: api-gateway8082
API Gateway配置:
spring:
application:
# 指定application name
name: api-gateway
cloud:
gateway:
routes:
# 路由id
- id: user-service-provider
# 路由到的地址
uri: http://127.0.0.1:8083/api/users/{username}
# 设置谓词,路径匹配的进行路由
predicates:
- Path=/api/users/{username}
可能遇到:Unable to find RoutePredicateFactory with name Path ,谓词Path必须大写,而且等号之间不能有空格
4.2 Bean注册方式配置网关
除了配置文件,也可以通过配置类进行网关配置:
package com.example.springcloud.gateway.configuration;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* <pre>
* spring cloud gateway configuration
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/09/14 15:00 修改内容:
* </pre>
*/
@Configuration
public class GatewayConfiguration {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
return routeLocatorBuilder.routes()
.route("user-service-provider1",
r->r.path("/api/findUser").uri("http://127.0.0.1:8083/api/findUser"))
.build();
}
}
4.3 CURL测试Gateway的接口
curl http://127.0.0.1:gateway_port/api/users/admin
5、Gateway谓词工厂分类
SpringCloud Gateway的谓词工厂分类如图,所谓谓词或者说断言,其实就是一种匹配的规则,根据这些匹配,匹配到就经过过滤器
SpringCloud Gateway的谓词分类比较多,在SpringCloud官方手册也有进行比较详细的介绍,所以本文章挑几个进行介绍既可
- After谓词配置
在指定的datetime后触发过滤器
spring:
cloud:
gateway:
routes:
- id: after_route
uri: http://127.0.0.1:8083/api/findUser
predicates:
- After=2019-01-01T16:30:00+08:00[Asia/Shanghai]
Before谓词配置
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://127.0.0.1:8083/api/findUser
predicates:
- Before=2018-01-01T16:30:00+08:00[Asia/Shanghai]
Between谓词配置 ```yaml spring: cloud: gateway:
routes:
- id: between_route
uri: http://127.0.0.1:8083/api/findUser
predicates:
- Between=2018-01-01T16:30:00+08:00[Asia/Shanghai], 2019-01-01T16:30:00+08:00[Asia/Shanghai]
- Cookie谓词配置
加上cookie "chocolate=ch.p" 经过过滤器
```yaml
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://127.0.0.1:8083/api/findUser
predicates:
- Cookie=chocolate, ch.p
linux curl测试接口:
curl http://192.168.9.10:8082/api/findUser?username=nicky --cookie "chocolate=ch.p"
- Header谓词配置
加上请求头参数“X-Request-Id”, \d+标识数字
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://127.0.0.1:8083/api/findUser
predicates:
- Header=X-Request-Id, \d+
curl http://192.168.9.10:8082/api/findUser?username=nicky -H "X-Request-Id:123"
- HOST谓词配置
加上域名host匹配
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://127.0.0.1:8083/api/findUser
predicates:
- Host=**.csdn.net
curl http://192.168.9.10:8082/api/findUser?username=nicky -H "Host:smilenicky.csdn.net"
- Method谓词配置
method是http请求方式,eg:GET、POST、PUT等等
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://127.0.0.1:8083/api/findUser
predicates:
- Method=GET
curl -X POST http://192.168.9.30:8082/api/findUser?username=nicky
{"timestamp":"2020-09-15T02:37:30.224+00:00","status":405,"error":"Method Not Allowed","message":"","path":"/api/findUser"}
- Query谓词配置
请求参数匹配,url?username=???
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://127.0.0.1:8083/api/findUser
predicates:
- Query=username
curl http://192.168.9.30:8082/api/findUser?username=nicky
- 远程地址谓词
在远程机192.168.1.1调用接口,都会匹配
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: http://127.0.0.1:8083/api/findUser
predicates:
- RemoteAddr=192.168.1.1/22
curl http://192.168.9.30:8082/api/findUser?username=nicky
- 权重谓词配置
权重匹配比较像nginx的权重配置,8083 url接收80%的请求,8084接收20%请求
spring:
cloud:
gateway:
routes:
- id: weight_high
uri:http://127.0.0.1:8083
predicates:
- Weight=group1, 8
- id: weight_low
uri: http://127.0.0.1:8084
predicates:
- Weight=group1, 2
curl http://192.168.9.30:8082/api/findUser?username=nicky
6、Gateway过滤器类型分类
7、GatewayFilter工厂分类
SpringCloud的gatewayFilter有很多分类,详情参考官方手册,官方手册的介绍也相对比较详细,所以本博客挑几个介绍:
- PrefixPath GatewayFilter
这个网关过滤器是在请求链接时候加上前缀,如下配置,加上/api的前缀
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: http://127.0.0.1:8083
filters:
- PrefixPath=/api
curl http://192.168.9.30:8082/findUser?username=nicky
相当于:
curl http://192.168.9.30:8082/api/findUser?username=nicky
- AddRequestParameter GatewayFilter
AddRequestParameter GatewayFilter自动带上请求参数的过滤器,加上username=admin参数
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: http://127.0.0.1:8083
filters:
- AddRequestParameter=username, admin
curl http://192.168.9.30:8082/api/findUser
相当于:
curl http://192.168.9.30:8082/api/findUser?username=admin
- Hystrix GatewayFilter
Hystrix分布式服务容错保护的过滤器
pom加上配置:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
写个ResultBean类:
package com.example.springcloud.gateway.bean;
import lombok.Data;
import org.springframework.http.HttpStatus;
/**
* <pre>
* ResultBean
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/08/10 17:07 修改内容:
* </pre>
*/
@Data
public class ResultBean {
/**
* 状态
* */
private int status;
/**
* 描述
* */
private String desc;
/**
* 数据返回
* */
private Object data;
public ResultBean(int status, String desc, Object data) {
this.status = status;
this.desc = desc;
this.data = data;
}
public ResultBean(Object data) {
this.status = HttpStatus.OK.value();
this.desc = "处理成功";
this.data = data;
}
public static ResultBean ok(Object data) {
return new ResultBean(data);
}
public static ResultBean ok() {
return new ResultBean(null);
}
public static ResultBean badRequest(String desc,Object data) {
return new ResultBean(HttpStatus.BAD_REQUEST.value(), desc, data);
}
public static ResultBean badRequest(String desc) {
return new ResultBean(HttpStatus.BAD_REQUEST.value(), desc, null);
}
public static ResultBean serverError(String desc, Object data){
return new ResultBean(HttpStatus.INTERNAL_SERVER_ERROR.value(),"服务器内部异常:"+desc,data);
}
public static ResultBean serverError(String desc){
return new ResultBean(HttpStatus.INTERNAL_SERVER_ERROR.value(),"服务器内部异常:"+desc,null);
}
}
接口出错,回调这个接口,避免一直请求,造成服务雪崩
package com.example.springcloud.gateway.rest.controller;
import com.example.springcloud.gateway.bean.ResultBean;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <pre>
* HystrixRestController
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/09/15 11:46 修改内容:
* </pre>
*/
@RestController
public class HystrixRestController {
@GetMapping(value = {"/fallback"})
public ResultBean fallback() {
return ResultBean.badRequest("Hystrix fallback");
}
}
加上配置:
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: http://127.0.0.1:8083/api/findUser
predicates:
- Method=GET
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
关了服务请求,让接口报错:
curl http://192.168.9.10:8082/api/findUser?username=nicky
{"status":400,"desc":"Hystrix fallback","data":null}
ps:spring cloud gateway:Unable to find GatewayFilterFactory with name Hystrix
项目里有引入eureka客户端,跟其源码,可以指定其实是有引入Hystrix的,所以原本,我就不加上Hystrix配置,不过项目是一直有报错:spring cloud gateway:Unable to find GatewayFilterFactory with name Hystrix,所以maven查看jar,发现eureka client只是引入部分的jar:
所以总的来说,还是要自己配置;
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- Requestratelimiter GatewayFilter
requestratelimiter用于简单的实现限流,基于Redis实现
@Bean
@Primary
KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("username"));
}
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
KeyResolver需要加上@Primary
spring:
redis:
host: 127.0.0.1
password:
port: 6379
cloud:
gateway:
routes:
## RequestRateLimiter GatewayFilter简单限流
- id: requestratelimiter_route
uri: http://192.168.9.30:8083/api/findUser
predicates:
- Method=GET
filters:
- name: RequestRateLimiter
args:
# 每秒允许处理的请求数量
redis-rate-limiter.replenishRate: 1
# 每秒最大处理的请求数量
redis-rate-limiter.burstCapacity: 2
#每秒最大处理token数量
redis-rate-limiter.requestedTokens: 1
# 限流策略,对应策略的BeanName
key-resolver: "#{@ipKeyResolver}"
- Retry GatewayFilter
retry GatewayFilter是用于重试的过滤器
spring:
cloud:
gateway:
routes:
# RetryFilter 重试过滤器
- id: retry_test
uri: http://127.0.0.1:8083/api/findUser
predicates:
- Method=GET
filters:
- name: Retry
args:
retries: 3 # 重试次数
statuses: BAD_GATEWAY
methods: GET,POST
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
8、GlobalFilter工厂分类介绍
SpringCloud Gateway的GlobalFilter分类如图,详情可以参考官方手册,GlobalFilter是全局的过滤器,作用于所有的路由
8.1 全局过滤器分类
8.2 自定义全局过滤器
package com.example.springcloud.gateway.filter.global;
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.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Slf4j
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("custom global filter");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}
配置类进行配置:
@Bean
public GlobalFilter customFilter() {
return new CustomGlobalFilter();
}
9、SpringCloud官方手册和博客
9.1 SpringCloud Gateway官方手册
- SpringCloud 2.0系列的官方参考手册:
https://docs.spring.io/spring-cloud-config/docs/2.2.x/reference/html/ SpringCloud gateway的官方参考手册:
https://docs.spring.io/spring-cloud-gateway/docs/2.2.x-SNAPSHOT/reference/html9.2 SpringCloud Gateway优质参考博客
方志鹏大佬系列Spring Cloud博客:https://www.fangzhipeng.com/spring-cloud.html
- 使用Spring Cloud与Docker实战微服务:https://eacdy.gitbooks.io/spring-cloud-book/content/
- 程序员DD大佬系列Spring Cloud博客:http://blog.didispace.com/spring-cloud-learning/
- Spring Cloud GateWay 应用 -> 高可用:https://juejin.im/post/6854573221329846280#heading-6
- Spring Cloud Gateway:SpringCloud API网关服务:https://juejin.im/post/6844903982599684103