SpringCloud API网关
Zuul
API网关用于管理所有外部客户端的访问,可以对请求进行路由调度、校验过滤、负载均衡、熔断、聚合等,Spring Cloud基于Netflix Zuul实现了API网关组件Spring Cloud Zuul,其主要解决两大问题
- 路由规则与服务实例的维护:Zuul通过将其自身注册为Eureka下的应用获取所有其他微服务的实例信息,通过以服务名为ContextPath的方式创建路由映射
- 校验过滤在微服务架构中的冗余:Zuul在API网关服务上进行统一调用校验服务来对微服务接口做前置过滤,以实现对微服务接口的拦截和校验,其提供的过滤器机制可以创建各种校验过滤器来制定需要校验的请求的规则
简单的API网关例子
我们通过构建一个简单的API网关,简单地感受一下Zuul对于路由服务和校验过滤的处理方式
引入依赖:
1
2
3
4
5
6
7
8
9
10
11
12<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>通过查看zuul依赖可以看到其包含hystrix、ribbon、actuator等重要依赖,用来提供API网关线程隔离、断路器、负载均衡、监察管理端点等功能
开启API网关功能:在Spring Boot启动类添加@EnableZuulProxy注解
基本信息配置:
1
2
3
4
5
6# 应用配置
spring.application.name=api-gateway
server.port=5555
# 配置eureka服务中心地址
eureka.client.service-url.defaultZone:http://localhost:1111/eureka/路由配置:配置面向服务的路由,将路由路径path映射到某个服务而不是url地址,当外部请求到达API网关时,API网关会从注册中心获取所有服务以及他们的实例清单,因此可以将外部请求映射到某个服务的url路径上
1
2
3# 配置zuul路由信息
zuul.routes.apiv1.path=/apiv1/**
zuul.routes.apiv1.service-id=TEST-SERVICE过滤器配置:定义过滤器来实现对请求的拦截与过滤,具体来说是继承ZuulFilter抽象类并实现它定义的4个抽象函数,并在Spring Boot主类中注册其Bean对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48//继承ZuulFilter抽象类
public class AccessFilter extends ZuulFilter {
private static Logger log = LogManager.getLogger(AccessFilter.class);
//1.重写filterType函数,定义过滤器类型,决定过滤器在哪个生命周期执行
public String filterType() {
return "pre";
}
//2.重写filterType函数,定义过滤器执行顺序,决定多个过滤器执行顺序
public int filterOrder() {
return 0;
}
//3.重写shouldFilter函数,指定过滤器对于请求的适用范围,true表示对所有请求都过滤
public boolean shouldFilter() {
return true;
}
//4.重写run函数,过滤器的具体逻辑
public Object run() throws ZuulException {
//获取请求的上下文环境
RequestContext ctx=RequestContext.getCurrentContext();
//获取请求
HttpServletRequest request=ctx.getRequest();
log.info("send {} request to {}",request.getMethod(),request.getRequestURL().toString());
//获取请求中accessToken参数内容
Object accessToken=request.getParameter("accessToken");
if(accessToken==null){
log.warn("accessToken is empty!!!");
//setSendZuulResponsew为false让zuul过滤请求 不进行路由转发
ctx.setSendZuulResponse(false);
//设置返回码和返回信息
ctx.setResponseStatusCode(401);
ctx.setResponseBody("Access Token Is Empty");
return null;
}
log.info("access token ok!!!");
return null;
}
路由详解
服务路由配置:zuul.routes.
= 默认路由:为Zuul构建的API网关引入Eureka后,API网关会为Eureka中每个服务自动创建一个默认路由规则,具体为zuul.routes.
=/serviceName/** ,由于我们不希望所有开放服务都可以被外部访问到,我们可以设置zuul.ignored-services属性指定Zuul不对什么服务自动创建路由自定义路由:如我们想将路径/v1/TestService/**自动映射到有规则地加了版本名的服务TestService-v1上,需要通过自定义路由转换,具体来说是在API网关下增加Bean对象,使用PatternServiceRouteMapper构建的路由比默认路由映射规则优先级高
1
2
3
4
5
6
7
public PatternServiceRouteMapper serviceRouteMapper(){
return new PatternServiceRouteMapper(
"(?<name>^.+)-(?<version>v.+$)",
"${version}/${name}"
);
}路径匹配规则
路径通配符:在路径中,?匹配一个字符,匹配任意字符,*匹配任意字符和多级目录
忽略表达式:
路径匹配优先级:从路由匹配算法getMatchingRoute中可以看到匹配请求路径时是通过线性遍历的,因此与路由规则的编写顺序有关,由于properties配置内容无法保证有序,因此我们需要用YAML文件来配置,达到有序的路由定义
1
2
3
4
5
6
7
8zull:
routes:
apiv1-ext:
path: /apiv1/ext/**
service-id: TEST-SERVICE-EXT
apiv1:
path: /apiv1/**
service-id: TEST-SERVICE