今天我們結合上一個案例來聊聊SpringCloud中路由器-Zuul。Zuul其實也是一個過濾器或者說是一個網關。你可以想象成Zuul是一個包裹的集散中心,他負責分佈式系統中請求的分發映射和過濾等一系列操作。下面是SpringCloud官網(地址爲:https://springcloud.cc/spring-cloud-dalston.html#_router_and_filter_zuul)對其的一些介紹:
Zuul的路由或者過濾器規則的編寫基本是基於JVM所支持的語言都可以寫,很不錯的是SpringCloud裏面內置了Java和Groovy語言,所以這裏用的是Java來聊聊。
來先看看個架構圖:
說明一下這個架構圖:
1:服務的消費者(consumer)、服務的提供者(server-provider-1,server-provider-2)、路由服務的提供者(Zuul)都向服務註冊中心註冊。
2:服務的消費者在使用SpringCloud路由器之前,是直接調用服務的提供者。但是在加上路由器以後是直接調用路由器的服務。當用戶(consumer)的請求到路由器(Zuul)後,路由器根據提前配置好的路由規則然後進行匹配,將匹配到的服務轉發到相應的服務提供者的服務器上(server-provider-1或者server-provider-2上)。
好了接下來簡單的搭建一個Zuul路由服務。
第一步:創建SpringCloud的服務選擇相應的SpringCloud插件
第二步:配置application.properties文件
對路由規則的編寫說明一下:
1:上圖中的1處爲路由服務的名稱,這個的名稱可以隨意起,但要見名知意。起好名稱以後開始爲這個服務名稱指定映射路徑,這個映射路徑是在以後訪問的時候消費者使用的。
2:在4處中爲3處配置好的路由服務地址配置對應的服務提供者(也就是配置服務提供者在註冊中心的名稱)這個服務提供者必須在註冊中心註冊並健康。
3:在4處和6處的名稱是一樣的。因爲我有兩個名稱相同的服務開啓(相當於做負載均衡)。如果服務提供者的這名稱不一致那麼是指向兩個不同的服務無法做負載均衡。
第三步:在啓動主類上添加相應的註解並啓動
第四步:在瀏覽器中訪問http://localhost:6080/server-1/getUserMassage?username=XX或者http://localhost:6080/server-2/getUserMassage?username=YY,就可以看到訪問不同的路由地址就可以調用不同的服務的提供者。
路由的最大好處是在於按需導引,靈活性和安全性更高。接下來看一下Zuul的過濾器。在說過濾器之前呢,先看這個架構圖:
上面就是Zuul過濾器的架構圖。對這個圖進行一個簡單的說明,上圖中有四種過濾位置不同的過濾器,四個顏色分別代表四種不同的過濾位置。下面就是對過濾器的一個簡單使用:
package com.alibaba; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.util.Map; @Component public class MyZuulFilter extends ZuulFilter { /** * 過濾器的類型 --> 返回一個字符串代表過濾器的類型,在zuul中定義了四種不同生命週期的過濾器類型,具體如下: * pre:可以在請求被路由之前調用 * route:在路由請求時候被調用 * post:在route和error過濾器之後被調用 * error:處理請求時發生錯誤時被調用 * @return */ @Override public String filterType() { return "pre"; } /** * 過濾的規則或者順序 * 通過int值來定義過濾器的執行順序 * 優先級爲0,數字越大,優先級越低 * @return */ @Override public int filterOrder() { return 0; } /** * 過濾器的狀態 * 返回一個boolean類型來判斷該過濾器是否要執行,所以通過此函數可實現過濾器的開關。 * 在上例中,我們直接返回true,所以該過濾器總是生效 * @return */ @Override public boolean shouldFilter() { return true; } /** * 過濾的邏輯 * @return * @throws ZuulException */ @Override public Object run() throws ZuulException { try{ //RequestContext對象是基於本地線程ThreadLocal,多個RequestContext對象之間 //是基於Map來調用 RequestContext requestContext = RequestContext.getCurrentContext(); //獲取request對象 HttpServletRequest httpServletRequest = requestContext.getRequest(); //根據獲取到的request對象可對用戶的請求參數或者路徑等進行過濾 //在實際的開發中一般是對用戶的請求路徑、請求參數、以及對請求參數中的值等進行過濾判斷 //接下來就逐個說一下 String requestURL = httpServletRequest.getServletPath(); String requestParam = httpServletRequest.getParameter("username"); Map<String,String[]> requestParamKeyUse = httpServletRequest.getParameterMap(); System.out.println("請求的路徑--->" + requestURL); System.out.println("請求的參數值--->" + requestParam); //上面是我們獲取到了,用戶的請求路徑裏面的一些參數,下面我們來看,如果用戶 //給我們沒有傳入指定的參數或者參數的值不合法那麼我們就根據不同的情況來分別的進行處理 //如果用戶的請求中沒有指定的參數那麼就返回一個400的錯誤並返回提示信息 if (!requestParamKeyUse.containsKey("username")){ requestContext.setSendZuulResponse(false);//false爲不進行路由,也就是不進行發送到服務提供者上去 requestContext.setResponseStatusCode(400);//返回錯誤碼 requestContext.getResponse().setCharacterEncoding("UTF-8");//設置相應編碼字符集 requestContext.getResponse().setContentType("text/html;charset=UTF-8");//設置頁面的編碼字符集 requestContext.setResponseBody("八戒說:當前的"+ requestURL +"請求沒有指定的參數"); return null; } //如果用戶的請求中有指定的參數但參數值非法就返回一個400錯誤並返回提示信息 if (!requestParamKeyUse.get("username")[0].equals("你好")){ requestContext.setSendZuulResponse(false); requestContext.setResponseStatusCode(400);//返回錯誤碼 requestContext.getResponse().setCharacterEncoding("UTF-8");//設置相應編碼字符集 requestContext.getResponse().setContentType("text/html;charset=UTF-8");//設置頁面的編碼字符集 requestContext.setResponseBody("八戒說:當前的"+ requestURL +"請求的參數不合法"); return null; } //如果用戶的請求沒有問題直接放行或者其他處理 requestContext.setSendZuulResponse(true); return null;//放行 }catch (Exception e){ System.out.println("MyZuulFilter/run Exception:" + e.getMessage()); return null; } } } 以上就是Zuul過濾器的使用。我們根據不同的業務場景和不同的需求可以實現不同位置而定過濾器,在運行過濾器的時候要注意把過濾器交給springBoot進行託管使用註解或者直接在啓動的主類中直接new出進行創建。