文档章节

Zuul源码阅读

OrangeJoke
 OrangeJoke
发布于 2018/10/02 14:31
字数 1238
阅读 518
收藏 6

Motivation

说一下,为什么要阅读Netflix Zuul,最近在看alibaba/Sentinel 在网关方面的应用。发现网关的设计模式有很多通用的地方。Netflix Zuul 对比Zuul2 更能直接的看出网关的主要功能和核心设计,易于上手,所以选择这个。同时也再次实践一下怎么高效阅读源代码。

Design and Function

Zuul 的主要功能,包括,鉴权,路由,流量监控,负载,实时响应(动态配置和开关),错误处理。

zuul

这张图就是根据这些功能提出的设计,通过Filter 将整个过程连接起来,

可扩展性:

  1. 在不同阶段做不同的处理,做隔离。
  2. 通过groovy 脚本加载来实现动态加载新的filter。

ZuulFilter

下面我们就来看总重要的 ZuulFilter 的设计吧,首先是类结构图

先看最基本的IZuulFilter 接口, 定义了两个方法,这个filter 要不要执行,怎么执行。

public interface IZuulFilter {
    /**
     * a "true" return from this method means that the run() method should be invoked
     *
     * @return true if the run() method should be invoked. false will not invoke the run() method
     */
    boolean shouldFilter();

    /**
     * if shouldFilter() is true, this method will be invoked. this method is the core method of a ZuulFilter
     *
     * @return Some arbitrary artifact may be returned. Current implementation ignores it.
	 * @throws ZuulException if an error occurs during execution.
     */
    Object run() throws ZuulException;

}

这里暂时提出疑问,throw ZuulException 会怎样?,返回任意result 怎么处理。

下面来看 Abstratct ZuulFilter类:

/**
 * Base abstract class for ZuulFilters. The base class defines abstract methods to define:
 * filterType() - to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
 * "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
 * We also support a "static" type for static responses see  StaticResponseFilter.
 * Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
 * <p/>
 * filterOrder() must also be defined for a filter. Filters may have the same  filterOrder if precedence is not
 * important for a filter. filterOrders do not need to be sequential.
 * <p/>
 * ZuulFilters may be disabled using Archius Properties.
 * <p/>
 * By default ZuulFilters are static; they don't carry state. This may be overridden by overriding the isStaticFilter() property to false
 *
 * @author Mikey Cohen
 *         Date: 10/26/11
 *         Time: 4:29 PM
 */
public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {

    private final AtomicReference<DynamicBooleanProperty> filterDisabledRef = new AtomicReference<>();

    /**
     * to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
     * "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
     * We also support a "static" type for static responses see  StaticResponseFilter.
     * Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
     *
     * @return A String representing that type
     */
    abstract public String filterType();

    /**
     * filterOrder() must also be defined for a filter. Filters may have the same  filterOrder if precedence is not
     * important for a filter. filterOrders do not need to be sequential.
     *
     * @return the int order of a filter
     */
    abstract public int filterOrder();
	
	public int compareTo(ZuulFilter filter) {
        return Integer.compare(this.filterOrder(), filter.filterOrder());
    }
}

根据设计图,我们知道Filter有三个阶段 分别是 pre, route,post, 这里的filterType 就是指定filter type(不同type的执行顺序不一样)。filterOrder 方法就是指定相同type 下filter的执行顺序了。同时也是实现 Comparable接口的原因了。

关于自定义 type,我们稍后再看。

那我们来看看 这些type的顺序是怎么指定的,

/**
 * Zuul Servlet filter to run Zuul within a Servlet Filter. The filter invokes pre-routing filters first,
 * then routing filters, then post routing filters. Handled exceptions in pre-routing and routing
 * call the error filters, then call post-routing filters. Errors in post-routing only invoke the error filters.
 * Unhandled exceptions only invoke the error filters
 *
 * @author Mikey Cohen
 *         Date: 10/12/11
 *         Time: 2:54 PM
 */
public class ZuulServletFilter implements Filter {

    private ZuulRunner zuulRunner;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        String bufferReqsStr = filterConfig.getInitParameter("buffer-requests");
        boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;

        zuulRunner = new ZuulRunner(bufferReqs);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {
            init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
            try {
                preRouting();
            } catch (ZuulException e) {
                error(e);
                postRouting();
                return;
            }
            
            // Only forward onto to the chain if a zuul response is not being sent
            if (!RequestContext.getCurrentContext().sendZuulResponse()) {
                filterChain.doFilter(servletRequest, servletResponse);
                return;
            }
            
            try {
                routing();
            } catch (ZuulException e) {
                error(e);
                postRouting();
                return;
            }
            try {
                postRouting();
            } catch (ZuulException e) {
                error(e);
                return;
            }
        } catch (Throwable e) {
            error(new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_FROM_FILTER_" + e.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }
}	

现在 看到了,ZuulFilter是通过实现在Servlet Filter的生命周期中实现的。那现在我们可以猜想 preRouting() 里会把所有的pre Type 按顺序执行:

    /**
     * runs all "pre" filters. These filters are run before routing to the orgin.
     *
     * @throws ZuulException
     */
    public void preRoute() throws ZuulException {
        try {
            runFilters("pre");
        } catch (ZuulException e) {
            throw e;
        } catch (Throwable e) {
            throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
        }
    }

    /**
     * runs all filters of the filterType sType/ Use this method within filters to run custom filters by type
     *
     * @param sType the filterType.
     * @return
     * @throws Throwable throws up an arbitrary exception
     */
    public Object runFilters(String sType) throws Throwable {
        if (RequestContext.getCurrentContext().debugRouting()) {
            Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
        }
        boolean bResult = false;
        List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                ZuulFilter zuulFilter = list.get(i);
                Object result = processZuulFilter(zuulFilter);
                if (result != null && result instanceof Boolean) {
                    bResult |= ((Boolean) result);
                }
            }
        }
        return bResult;
    }

从代码可以看出process根据列表里的filter顺序执行,顺序在返回前就按order 排序了,这样,当一个 preFilter 执行抛出 ZuulException 之后,pre阶段就结束了,进到最初ServletFilter的后续阶段。

所以到这里为止,我们就知道Zuul是怎么工作和扩展的了,大家就可以根据自己的需要进行扩展了。

© 著作权归作者所有

OrangeJoke
粉丝 40
博文 57
码字总数 39192
作品 0
江北
高级程序员
私信 提问
实用技巧:快速定位Zuul的性能瓶颈

Zuul的性能不是特别好,特别是,某些项目对Zuul进行了一些扩展,代码还不那么考究时。 如何快速定位出Zuul的性能瓶颈呢?我们知道,Zuul的核心是过滤器,Zuul大多功能都是基于过滤器实现的。...

周立_ITMuch
2019/03/25
633
0
[学习微服务-第2天] ServiceComb + SpringCloud Zuul源码解读

上一篇文章我们介绍了ServiceComb与SpringCloud的Zuul网关组件协同工作,以构建微服务应用。为了给ServiceComb做贡献的伙伴提供指引,本篇将介绍ServiceComb与SpringCloud Zuul的集成源码。 ...

业界首个Apache微服务顶级项目
2019/02/12
79
0
Spring Cloud Zuul的fallback优化

如何在Zuul中使用fallback功能 我们在项目中使用Spring cloud zuul的时候,有一种这样的需求,就是当我们的zuul进行路由分发时,如果后端服务没有启动,或者调用超时,这时候我们希望Zuul提供...

李刚
2017/11/15
0
0
Spring Cloud Zuul重试机制探秘

简介 本文章对应spring cloud的版本为(Dalston.SR4),具体内容如下: 开启Zuul功能 通过源码了解Zuul的一次转发 怎么开启zuul的重试机制 Edgware.RC1版本的优化 开启Zuul的功能 首先如何使用...

李刚
2017/11/17
0
0
Spring Cloud构建微服务架构:服务网关(过滤器)【Dalston版】

在前两篇文章:服务网关(基础)、服务网关(路由配置)中,我们了解了Spring Cloud Zuul作为网关所具备的最基本功能:路由。本文我们将具体介绍一下Spring Cloud Zuul的另一项核心功能:过滤...

程序猿DD
2017/09/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring5 依赖注入和循环依赖处理

//TODO populateBean 注入属性 doGetBean->getSingleton 删除bean缓存

小小明1995
25分钟前
67
0
每天AC系列(七):合并两个有序链表

1 题目 LeetCode第21题,合并两个有序链表. 2 直接合并 因为已经有序了,类似归并排序中的合并一样,操作不难,直接上代码了. ListNode t = new ListNode(0);ListNode head = t;while(l1 != nu...

Blueeeeeee
28分钟前
47
0
数据结构之双向链表-c语言实现

原文链接:http://blog.seclibs.com/%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e4%b9%8b%e5%8f%8c%e5%90%91%e9%93%be%e8%a1%a8-c%e8%af%ad%e8%a8%80%e5%ae%9e%e7%8e%b0/ 这次完成了双向链表的代......

无心的梦呓
29分钟前
65
0
Check If a String Is Numeric in Java

1. Introduction Oftentimes while operating upon Strings, we need to figure out whether a String is a valid number or not. In this tutorial, we’ll explore multiple ways to detec......

Ciet
35分钟前
43
0
SpringCloud 基础教程(六)-负载均衡Ribbon

 我的博客:兰陵笑笑生,欢迎浏览博客!  上一章 SpringCloud基础教程(五)-配置中心热生效和高可用当中,我们对配置中心进行进行了深入的了解,本章将继续微服务架构的深入学习,了解在微服务...

_兰陵笑笑生
今天
49
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部