在設計系統階段,必定記住Zuul的核心思想:Zuul是作服務的路由,而不是頁面的轉發。這是zuul與Nginx根本上的不一樣。前端
Zuul沒有提供良好的跳轉或轉發功能,這和它的使用場景是有關的。它的核心功能包括驗證服務權限,驗證成功或失敗,成功或失敗後放行仍是攔截等等。若攔截,只需返回響應碼便可,至於成功或失敗的頁面顯示,則應該由前端負責跳轉或轉發。
請必定遵照標準化的設計,不要讓Zuul參與頁面跳轉(固然,Zuul也能夠實現頁面跳轉,本文後半部分會涉及到,但不推薦由Zuul實現跳轉)網絡
Zuul的驗證功能基本在它的過濾器中實現。ide
每個請求發送到Zuul,都有其對應的不一樣階段,能夠稱其爲「生命週期」,好比初始化階段,處理請求階段,結束階段,出error階段等等。微服務
在談下一話題前,請必定分清楚「轉發」和「路由」的區別。
若是說定義,路由是一種策略,轉發是一條路徑。
說淺顯一點,路由是網狀結構下的各類路徑解的集合,而轉發是點對點的一條通訊路徑。
再淺顯一些,就像學生年代你上課時給女神小紅遞紙條,路由是你將紙條給你同桌(代理),你同桌會選擇一條消息發送的路徑,好比將紙條傳遞給A,再到B,再到C,最後到小紅。由同桌發到小紅的整個過程就叫路由。你只須要將紙條給同桌(代理),剩下的不須要你參與,你的同桌會選擇最優路徑,不管是經過ABC,仍是BAC,當紙條傳遞成功,你的代理會反饋給你結果(同桌會告訴你紙條傳遞成功,小紅看都沒看就扔進垃圾桶),你不須要知道中間隔了多少人。這就是路由。
而轉發,就是你直接把紙條揉成團,扔給小紅,小紅收到後含羞一笑,你看到後心花盛開。這就是轉發。
最淺顯的歸納就是: 路由是多跳的轉發,轉發的一跳的路由。這一點從英文routing和forwading的區別就能夠明白。
若是還沒法理解,請從新讀一遍計算機網絡。工具
對於微服務項目來講,Zuul實現的就是路由功能,每每也被會稱作路由轉發、網關等等,但意思都是同樣的。
本文在談Zuul的攔截器,
每個request在發送到Zuul後,都有幾個不一樣的階段(生命週期),Zuul的幾個攔截器對應request的不一樣階段,因此咱們先談一下request的生命週期:post
以上四個階段是request的生命週期,Zuul的四種攔截器分別對應以上的四個request生命週期: PRE, ROUTING, POST, ERROR.計算機網絡
因此,若是想要對request的某個生命週期進行操做(過濾),只須要按需實現這四種攔截器便可。設計
那麼,如何實現這四種攔截器呢?依然超級簡單,答案只有一個:繼承ZuulFilter
類並覆寫父類方法便可。
區分攔截器的類型在方法filterType()
中。
閒言少敘,上代碼。
如下是一個最基本的PRE攔截器。代理
定義
新建一個類,繼承ZuulFilter,並覆寫其方法。code
public class PreRequestFilter extends ZuulFilter { private static final Logger logger = LoggerFactory.getLogger(RouteRequestFilter.class); // 表示此攔截器類型, pre、route、post、error @Override public String filterType() { return "pre"; } // 執行順序,如有多個同種攔截器,好比有多個pre攔截器,他們的執行順序是什麼 @Override public int filterOrder() { return 0; } // 是否應用這個攔截器,true是應用,false表示這個攔截器是失效的 @Override public boolean shouldFilter() { return false; } // 此方法爲核心方法,表示攔截器中執行的邏輯 @Override public Object run() throws ZuulException { System.out.println("pre"); return null; } }
註冊爲Bean
在主類中將此攔截器註冊爲Bean
@EnableZuulProxy @SpringBootApplication public class ZuulApp { public static void main(String[] args) { SpringApplication.run(ZuulApp.class, args); } @Bean public PreRequestLogFilter preRequestLogFilter(){ return new PreRequestLogFilter(); }
此時,一個最基本的攔截器就建立成功了!
在run方法中,添加如下內容:
@Override public Object run(){ //獲取上下文 RequestContext ctx = RequestContext.getCurrentContext(); //獲取Request HttpServletRequest request = ctx.getRequest(); //獲取請求參數accessToken String accessToken = request.getParameter("accessToken"); //使用String工具類 if (StringUtils.isBlank(accessToken)) { logger.error("no token"); logger.warn("accessToken is empty"); ctx.setSendZuulResponse(false); //進行攔截 ctx.setResponseStatusCode(401); try { ctx.getResponse().getWriter().write("accessToken is empty"); } catch (IOException e) { e.printStackTrace(); } return null; } return null; }
此時,若經過Zuul訪問微服務,若沒有accessToken參數,會發現請求被攔截,頁面上會顯示「accessToken is empty」