Zuul源碼分析

先上一張流程圖:

我們Zuul的使用如下:

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class);
    }
}

application.properties配置:

zuul.routes.study-trade.service-id=study-trade
zuul.routes.study-trade.path=/orderservice/**
  1. 從我們的開啟Zuul註解@EnableZuulProxy開始看起,這個比較簡單,就是引入了ZuulProxyMarkerConfiguration.Marker這個Bean。

  2. 再找到Zuul這個包下面的spring.factories這個文件,裡面有兩個類,我們看一下

  3. 有一個是ZuulServerAutoConfiguration類,它裡面初始化了ZuulHandlerMapping,ZuulController,ZuulServlet。還有一個zuulProperties變數,它會將我們application.yml文件里配置的路由映射規則讀進來

  4. 而另一個類ZuulProxyAutoConfiguration,他重要的一點是會初始化RibbonRoutingFilter,PreDecorationFilter,SimpleHostRoutingFilter

  5. 以上就是應用初始化相關的準備

  6. 當我們請求過來時會怎麼樣呢?

我使用orderservice前綴來訪問study-trade服務://localhost:8000/orderservice/trade/testTrade/3
7. 請求發送過來走到ZuulHandlerMapping,並調用到registerHandlers方法,routes就是我application.yml里配置的映射關係

private void registerHandlers() {
		Collection<Route> routes = this.routeLocator.getRoutes();
		if (routes.isEmpty()) {
			this.logger.warn("No routes found from RouteLocator");
		}
		else {
			for (Route route : routes) {
			    //註冊到
				registerHandler(route.getFullPath(), this.zuul);
			}
		}
	}
  1. 在走到DispatcherServlet里的doDispatch方法,然後使用ZuulController取處理請求
//mappedHandler.getHandler()就是ZuulController
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  1. ZuulController接收到請求,會使用ZuulServlet來處理請求,他的service方法如下:
 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,route。我們自己定義的Filter也是通過這裡的程式碼得以執行的。
10. 點開ZuulServlet的preRoute等這幾個方法時,看到他其實又是使用ZuulRunner來處理的

void preRoute() throws ZuulException {
        zuulRunner.preRoute();
    }

   
void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
        zuulRunner.init(servletRequest, servletResponse);
    }
  1. 以preRoute()方法為例,它裡面又是使用了FilterProcessor.getInstance()來處理方法,看 FilterProcessor.getInstance()樣子,感覺是一個單例,點進去看了下,確實是使用了餓漢模式的單例。
 public void preRoute() throws ZuulException {
        FilterProcessor.getInstance().preRoute();
    }
  1. 接下來一起探究下FilterProcessor類吧
 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());
        }
    }
    
 public Object runFilters(String sType) throws Throwable {
        if (RequestContext.getCurrentContext().debugRouting()) {
            Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
        }
        boolean bResult = false;
        //找到相應sType的ZuulFilter集合,然後執行run方法
        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;
    }

相當於是FilterProcessor從filterloader中獲取zuulfilter。而zuulfilter是被filterFileManager所載入,並支援groovy熱載入,採用了輪詢的方式熱載入。有了這些filter之後,zuulservelet首先執行的Pre類型的過濾器,再執行route類型的過濾器,最後執行的是post類型的過濾器,如果在執行這些過濾器有錯誤的時候則會執行error類型的過濾器。執行完這些過濾器,最終將請求的結果返回給客戶端。 RequestContext就是會一直跟著整個請求周期的上下文對象,filters之間有什麼資訊需要傳遞就set一些值進去就行了。

  1. 那麼是怎麼調用其他服務的呢?其中有一個RibbonRoutingFilter,就實現了調用其他服務的方法,具體內容不表
  2. 附上一張更全的架構設計圖