zuul-開篇

一彪标、簡要介紹

zuul wiki鏈接

Zuul is the front door for all requests from devices and web sites to the backend of the Netflix streaming application. As an edge service application, Zuul is built to enable dynamic routing, monitoring, resiliency and security. It also has the ability to route requests to multiple Amazon Auto Scaling Groups as appropriate.

這里有幾個關(guān)鍵詞

  • front door
    我們這里可以理解為請求網(wǎng)關(guān)他爸,在底層服務(wù)和客戶端/網(wǎng)頁之間的通訊橋梁。我個人對zuul的理解更偏向于業(yè)務(wù)總線,服務(wù)編排冰垄,整合底層的各種基礎(chǔ)服務(wù)滑绒。
  • dynamic routing
    動態(tài)路由
  • resiliency
    可伸縮
  • security
    安全性
    wiki里面提到的3個使用場景
  • Surgical Routing
  • Stress Testing
  • Multi-Region Resiliency
    其實我個人認為有點夸大的成分,我理解這些都是動態(tài)路由的應(yīng)用場景而已骚勘,而zuul的動態(tài)路由也只是說把攔截器的邏輯用groovy來寫铐伴,通過這種方式來實現(xiàn)熱部署撮奏,并不是什么稀奇的東西。

二当宴、源碼下載&啟動

git地址
官方安裝wiki

  1. 源碼下載并編譯
  2. 修改配置文件
    application.properties
zuul.filters.root=zuul-sample/src/main/groovy/com/netflix/zuul/sample/filters

filter的根目錄修改一下

  1. 進程啟動配置jvm的系統(tǒng)參數(shù)
-DTZ=GMT -Darchaius.deployment.environment=test -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false -Deureka.validateInstanceId=false -Deureka.mt.num_retries=1
  1. 運行com.netflix.zuul.sample.Bootstrap啟動類
Zuul Sample: starting up.
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Zuul Sample: finished startup. Duration = 26856 ms
2019-02-15 00:38:59,487 WARN  io.netty.bootstrap.ServerBootstrap [main] Unknown channel option 'SO_KEEPALIVE' for channel '[id: 0x283bb17c]'
2019-02-15 00:38:59,487 WARN  io.netty.bootstrap.ServerBootstrap [main] Unknown channel option 'SO_LINGER' for channel '[id: 0x283bb17c]'
2019-02-15 00:38:59,487 WARN  io.netty.bootstrap.ServerBootstrap [main] Unknown channel option 'TCP_NODELAY' for channel '[id: 0x283bb17c]'

三畜吊、核心組件分析

這里對zuul分析的版本是v1.1.0。為什么選擇zuul1的版本來分析户矢,主要是考慮到第一版的特性會比較少玲献,方便我了解zuul的核心功能。另外zuul1用的是BIO梯浪,代碼看起來會簡單不少捌年。zuul2增加了很多新的特性和優(yōu)化,后面會抽時間慢慢看完挂洛。


zuul的請求流程

接下來分析下zuul-simple-webapp

web.xml configures a few things:

  • StartServer as a ServletContextListener that initializes the app.
  • ZuulServlet is a servlet that matches all requests. It performs the core Zuul Filter flow of executing pre, routing, and post Filters.
  • ContextLifecycleFilter is a servlet filter matching all requests. It cleans up the RequestContextafter each request, ensuring isolation.
<listener>
    <listener-class>com.netflix.zuul.StartServer</listener-class>
</listener>

<servlet>
    <servlet-name>Zuul</servlet-name>
    <servlet-class>com.netflix.zuul.http.ZuulServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>Zuul</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>ContextLifecycleFilter</filter-name>
    <filter-class>com.netflix.zuul.context.ContextLifecycleFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>ContextLifecycleFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
1. StartServer.java
private void initGroovyFilterManager() {
        FilterLoader.getInstance().setCompiler(new GroovyCompiler());

        String scriptRoot = System.getProperty("zuul.filter.root", "");
        if (scriptRoot.length() > 0) scriptRoot = scriptRoot + File.separator;
        try {
            FilterFileManager.setFilenameFilter(new GroovyFileFilter());
            FilterFileManager.init(5, scriptRoot + "pre", scriptRoot + "route", scriptRoot + "post");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

這里定義了groovy的編譯器和Zuul的Filter的加載路徑礼预。

2. ZuulFilter的定義

zuulFilter即為zuul的過濾器實現(xiàn)。我們后續(xù)需要實現(xiàn)的各種過濾器都必須基于這個類來實現(xiàn)虏劲。接口比較簡單托酸,注釋也寫得很好,這里就不展開來講伙单。

public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {

    private final DynamicBooleanProperty filterDisabled =
            DynamicPropertyFactory.getInstance().getBooleanProperty(disablePropertyName(), false);

    /**
     * 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();

    /**
     * By default ZuulFilters are static; they don't carry state. This may be overridden by overriding the isStaticFilter() property to false
     *
     * @return true by default
     */
    public boolean isStaticFilter() {
        return true;
    }

    /**
     * The name of the Archaius property to disable this filter. by default it is zuul.[classname].[filtertype].disable
     *
     * @return
     */
    public String disablePropertyName() {
        return "zuul." + this.getClass().getSimpleName() + "." + filterType() + ".disable";
    }

    /**
     * If true, the filter has been disabled by archaius and will not be run
     *
     * @return
     */
    public boolean isFilterDisabled() {
        return filterDisabled.get();
    }
3. ZuulServlet.java

接下來看ZuulServlet,這部分是zuul最核心的邏輯获高。

@Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

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

        zuulRunner = new ZuulRunner(bufferReqs);
    }

首先會初始化ZuulRunner,其中bufferReqs這個屬性其實就是控制Request是否使用bufferReader吻育,允許多次從request里面讀取內(nèi)容念秧。

ZuulRunner.java

/**
     * sets HttpServlet request and HttpResponse
     *
     * @param servletRequest
     * @param servletResponse
     */
    public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        RequestContext ctx = RequestContext.getCurrentContext();
        if (bufferRequests) {
            ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
        } else {
            ctx.setRequest(servletRequest);
        }

        ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
    }

接下里我們看zuulSerlvet的核心邏輯。

@Override
    public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
        try {
            init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

            // Marks this request as having passed through the "Zuul engine", as opposed to servlets
            // explicitly bound in web.xml, for which requests will not have the same data attached
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();

            try {
                preRoute();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                route();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                postRoute();
            } catch (ZuulException e) {
                error(e);
                return;
            }

        } catch (Throwable e) {
            error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }
  • preRoute和postRoute不管發(fā)生任何異常的情況下都會執(zhí)行布疼。如果在執(zhí)行
    preRoute的時候就發(fā)生異常摊趾,會跳過route的邏輯,直接到postRoute游两。
  • 不管在哪一步發(fā)生異常都會執(zhí)行error的過濾器砾层。

另外RequestContext的作用為了保存請求和執(zhí)行結(jié)果的上下文,方便在過濾器中傳遞贱案。包括過濾器之間如果需要做傳輸傳遞的話也是依賴RequestContext來實現(xiàn)肛炮。

5.FilterProcessor.java

最后我們再看下執(zhí)行過濾器的邏輯,實時上就是根據(jù)過濾器的類型拿到過濾器的鏈表宝踪,遍歷執(zhí)行

/**
     * 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;
    }

三侨糟、總結(jié)

zuul的代碼不多,整體的解決思路有點類似spring的攔截器實現(xiàn)瘩燥,只是spring攔截器一般面向的是方法秕重,zuul建議面向的是服務(wù)(當(dāng)然這個看個人的使用方式)。另外zuul使用允許過濾器使用groovy進行動態(tài)編譯注入厉膀,不需要發(fā)版溶耘。我認為在解決問題的思路上并不是一個新的思路二拐,只是說在分布式的場景下的一個應(yīng)用場景罷了。當(dāng)然zuul2增加了很多新的特性凳兵,這個是需要我這邊去深入了解的百新。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市留荔,隨后出現(xiàn)的幾起案子吟孙,更是在濱河造成了極大的恐慌,老刑警劉巖聚蝶,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異藻治,居然都是意外死亡碘勉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門绘迁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來楼吃,“玉大人能真,你說我怎么就攤上這事∈どぃ” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵钩乍,是天一觀的道長辞州。 經(jīng)常有香客問我,道長寥粹,這世上最難降的妖魔是什么变过? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮涝涤,結(jié)果婚禮上媚狰,老公的妹妹穿的比我還像新娘。我一直安慰自己阔拳,他們只是感情好崭孤,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著糊肠,像睡著了一般辨宠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上罪针,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天彭羹,我揣著相機與錄音,去河邊找鬼泪酱。 笑死派殷,一個胖子當(dāng)著我的面吹牛还最,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播毡惜,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼拓轻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了经伙?” 一聲冷哼從身側(cè)響起扶叉,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帕膜,沒想到半個月后枣氧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡垮刹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年达吞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荒典。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡酪劫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寺董,到底是詐尸還是另有隱情覆糟,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布遮咖,位于F島的核電站滩字,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盯滚。R本人自食惡果不足惜踢械,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望魄藕。 院中可真熱鬧内列,春花似錦、人聲如沸背率。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寝姿。三九已至交排,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間饵筑,已是汗流浹背埃篓。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留根资,地道東北人架专。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓同窘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親部脚。 傳聞我的和親對象是個殘疾皇子想邦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359