一. 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也可以看一下, 然后看看他們之間的加載順序.