本文篇幅较长,前面一、二部分主要讲原理跟源码,需要直接看实现的小伙伴可以直接通过目录跳转到第三部分
目录
1. Zuul请求流程
Zuul是一个基于Servlet的的网关组件,Zuul定义了四种类型的拦截器,分别是前置拦截器(pre)、路由拦截器(route)、后置拦截器(post)、异常拦截器(error)
而框架本身的路由功能也是基于拦截器实现的
当开启@EnableZuulProxy时,框架会在服务启动时向容器中注入一批内置的拦截器,其中就包括路由相关的拦截器。
当一个请求进来时,会依次进入这些拦截器,并由拦截器实现请求的转发。
下面是在路由功能中,一个请求的大致请求流程
可以看到,一个请求进来之后,它首先会在前置拦截器中被判断是否符合已存在的路由规则,如果请求路径与路由规则相匹配,那么这个请求会被打上一些相关的标记,然后在进入框架由内置的route拦截器时实现请求的转发
这里涉及到一个很重要的点,那就是:前置拦截器在对请求进行路由规则匹配时,首先要先获取到路由规则列表,所以说,这个前置拦截器一定要从某处加载路由规则。所以,要想实现路由规则的动态管理,我们主要的思路就在于
- 找到Zuul在何处加载路由规则
- 复写它的加载逻辑,使其可以从我们定义的数据源加载路由
所以要先看一下Zuul它内置了什么拦截器,以及拦截器是如何生效的
2. Zuul内置路由拦截器源码解读
2.1 框架拦截器说明
Zuul实现请求路由主要依靠以下三个拦截器:
org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter
前置拦截器,判断请求是否与现有路由规则相匹配,如果匹配则往请求容器中注入相关路由信息,以便在实际进行路由时能获取到目标路由地址
org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter
从注册中心上自动发现的服务路由规则以及配置的服务路由,都由该路由拦截器进行转发。
例:
zuul:
routes:
demo1:
path: /demo1/**
serviceId: demo1
stripPrefix: false
当访问
http://localhost:8080/demo1/test
时,会进入该拦截器,这个拦截器会去注册中心上寻找服务名为demo1服务,并将请求转发到这个服务上,
org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter
配置了外部链接的路由,则由该路由进行转发,默认实现是调用httpClient向路由地址发送http请求获取响应
例:
zuul:
routes:
demo:
path: /demo/**
url: http://localhost:8081
stripPrefix: false
当访问:
- http://localhost:8080/demo/test
时,就会进入该拦截器,这个拦截器会根据已配置的路由规则向
- http:localhost:8081/demo/test
发送请求
2.2 源码剖析
查看PreDecorationFilter的源码
@Override
public Object run() {
// 获取请求容器(存放于ThreadLocal中,线程独有)
RequestContext ctx = RequestContext.getCurrentContext();
// 获取请求uri
final String requestURI = this.urlPathHelper
.getPathWithinApplication(ctx.getRequest());
// 重点:判断请求uri是否与现有路由匹配
Route route = this.routeLocator.getMatchingRoute(requestURI);
// 如果匹配上的话,则往请求容器设置一些路由相关的信息,例如路由目标地址,前缀之类的
if (route != null) {
String location = route.getLocation();
if (location != null) {
ctx.put(REQUEST_URI_KEY, route.getPath());
ctx.put(PROXY_KEY, route.getId());
/***********省略一堆非主要代码*************/
}
}
else {
log.warn("No route found for uri: " + requestURI);
String forwardURI