4. 网关的原理及源码
一. zuul路由的原理
从客户端的请求过来了, 全部走网关, 网关经过处理, 将请求分发给微服务应用, 微服务处理完请求以后, 将结果返回给网关. 网关在响应给客户端.
二. 我们怎么实现zuul的效果?
用户访问进来 -> preRouterFilter -> routerFilter -> PostRouterFilter -> 微服务
zuul的核心本质就是filter, Zuul的所有功能都是在Filter里面实现
Zuul 解析我们的url来决定我们去访问哪个微服务, 这是一个过滤器
Zuul 去发请求访问微服务, 这也是一个过滤器
Zuul 给用户响应数据, 这也是一个过滤器
1. 四种过滤器的关系
这是一个网关过滤器, 包含四种过滤器类型.
当一个请求过来的时候, 他会先进入pre, 然后进入routing , 最后进入post过滤器. 三个过滤器任何一个发生异常, 都进入error routing过滤器
2. 共享RequestContext
zuul中所有的filter在同一个线程里共享RequestContext.
zuulFilter通过RequestContext共享访问的变量
三. 用户访问zuul的入口
思考这个问题, 我们可以类别springMVC. 在springMVC中, 用户请求的入口会进入到哪里呢? DispatcherServlet.
那么zuul也是一样的, 他的入口也是servlet, 名字叫做ZuulServlet
下面, 我们来看一下zuul-core包的核心代码
在这里面有一个http文件夹, 在http文件中就是处理用户过来的请求的
zuul的入口是zuulServlet
在servlet中, 我们知道其执行的主方法是service. 因此我们来看看主要的核心方法service()
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { try {
// 初始化http请求 this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
// 所有请求都会建立一个RequestContent RequestContext context = RequestContext.getCurrentContext();
// 给当前的访问, 设置一个处理引擎 context.setZuulEngineRan(); try {
// 首先执行的是前置过滤器, 从所有过滤器中过滤出pre类型的过滤器, 并执行
// 前置过滤器通常处理用户参数校验, 权限校验, 限流, 熔断等 this.preRoute(); } catch (ZuulException var12) {
// 如果pre filter发生异常, 则进入error过滤器 this.error(var12);
// 在进入post route过滤器
// 用户的响应数据是通过post filter返回给客户端的 this.postRoute(); return; } try {
// route filter的主要工作是, 将用户的请求, 转变成有zuul构造的请求, 去访问微服务 this.route(); } catch (ZuulException var13) { this.error(var13); this.postRoute(); return; } try {
// 将微服务响应的数据, 返回给客户端 this.postRoute(); } catch (ZuulException var11) { this.error(var11); } } catch (Throwable var14) { this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName())); } finally { RequestContext.getCurrentContext().unset(); } }
我们来看一下源码.
源码流程如上
下面我们以前置过滤器为例, 说明过滤器执行的原理
四. zuul有哪些关键的filter?
我们来看看都spring自己加载的过滤器有哪些?
spring会自动扫描注解, 加载一下了两个类的所有过滤器
ZuulProxyAutoConfiguration 类里面看核心的关键filter
ZuulServerAutoConfiguration 里面的核心的关键filter
这是在ZuulProxyAutoConfiguration注册的过滤器
这是在ZuulServierAutoConfiguration中注册的过滤器
汇总:
pre过滤器:
PreDecorationFilter
ServletDetectionFilter
FormBodyWrapperFilter
DebugFilter
Servlet30WrapperFilter
routing 过滤器
RibbonRoutingFilter
SimpleHostRoutingFilter
post过滤器
SendResponseFilter
SendForwardFilter
error过滤器
SendErrorFilter
其实有这么多个过滤器, error过滤器就是不说, 有异常会进入到error过滤器
那么其他过滤器中最重要的就是一下三个.
一个用户请求过了, 首先要有一个前置过滤器解析连接, 组装路由.
然后执行route 过滤器跳转到指定的微服务
最后执行post过滤器,将执行的结果返回给用户
1. 下面我们来看看preDecorationFilter过滤器做了什么
通过看源码,我们得到以上信息, 其实这里主要做了一件事, 那就是梳理出后面要跳转到那个微服务的路由信息
@Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); final String requestURI = this.urlPathHelper .getPathWithinApplication(ctx.getRequest()); 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()); if (!route.isCustomSensitiveHeaders()) { this.proxyRequestHelper.addIgnoredHeaders( this.properties.getSensitiveHeaders().toArray(new String[0])); } else { this.proxyRequestHelper.addIgnoredHeaders( route.getSensitiveHeaders().toArray(new String[0])); } if (route.getRetryable() != null) { ctx.put(RETRYABLE_KEY, route.getRetryable()); } if (location.startsWith(HTTP_SCHEME + ":") || location.startsWith(HTTPS_SCHEME + ":")) { ctx.setRouteHost(getUrl(location)); ctx.addOriginResponseHeader(SERVICE_HEADER, location); } else if (location.startsWith(FORWARD_LOCATION_PREFIX)) { ctx.set(FORWARD_TO_KEY, StringUtils.cleanPath( location.substring(FORWARD_LOCATION_PREFIX.length()) + route.getPath())); ctx.setRouteHost(null); return null; } else { // set serviceId for use in filters.route.RibbonRequest ctx.set(SERVICE_ID_KEY, location); ctx.setRouteHost(null); ctx.addOriginResponseHeader(SERVICE_ID_HEADER, location); } if (this.properties.isAddProxyHeaders()) { addProxyHeaders(ctx, route); String xforwardedfor = ctx.getRequest() .getHeader(X_FORWARDED_FOR_HEADER); String remoteAddr = ctx.getRequest().getRemoteAddr(); if (xforwardedfor == null) { xforwardedfor = remoteAddr; } else if (!xforwardedfor.contains(remoteAddr)) { // Prevent duplicates xforwardedfor += ", " + remoteAddr; } ctx.addZuulRequestHeader(X_FORWARDED_FOR_HEADER, xforwardedfor); } if (this.properties.isAddHostHeader()) { ctx.addZuulRequestHeader(HttpHeaders.HOST, toHostHeader(ctx.getRequest())); } } } else { log.warn("No route found for uri: " + requestURI); String forwardURI = getForwardUri(requestURI); ctx.set(FORWARD_TO_KEY, forwardURI); } return null; }
2. RibbonRoutintFilter过滤器
这是zuul中非常重要的一个过滤器, 他是执行路由转发到微服务的工作. 具体的流程如上
3. SendResponseFilter过滤器
这个过滤器是将微服务响应的请求回传给用户
其他的filter也可以看一下, 然后看看他们之间的加载顺序.
