過濾器
Zuul作爲網關的其中一個重要功能, 就是實現請求的鑑權。而這個動作我們往往是通過Zuul提供的過濾器來實現的
ZuulFilter
ZuulFilter是過濾器的頂級父類。在這裏我們看一- 下其中定義的4個最重要的方法:
public abstract class ZuulFilter implements IZuulFilter {
public abstract String filterType();//過濾器類型
public abstract int filterOrder();//過濾器順序
public abstract boolean shouldFilter();//要不要過濾,來自IZuulFilter
public abstract Object run() throws ZuulException;//過濾邏輯,來自IZuulFilter
}
shouldFilter:返回一個boolean值,判斷該過濾器是否需要執行,返回true執行,返回false不執行。
run:過濾器的具體業務邏輯
filterType:返回字符串,代表過濾器的類型,包含以下4種:
pre:請求在被路由之前執行
routing:在路由請求時調用
post:在routing和error過濾器之後調用
error:處理請求時發生錯誤調用
filterOrder:通過返回的int值來定義過濾器的執行順序,數字越小優先級越高
過濾器執行生命週期
這張是Zuul官網提供的請求生命週期圖,清晰的表現了一次請求在各個過濾器的執行順序。
●“custom” filter是自定義filter可以任意指定其類型,
●“error” filter是當"pre” filter 、“routing” filter、 “post” filter任何一個出現問題時,會跳到這個“error” filter,它執行完畢後後返回給“post” filter最終返回給客戶端瀏覽器。
●流程細說:
一次http請求首先會到達 "pre” filter (內置過濾器) :主要是對請求進行處理解析,如:請求參數、頭的處理解析等。執行完畢後,說明request請求處理完成,請求已經準備就緒,就應該轉發給具體的微服務(Origin server 遠程服務)了。“routing” filter會根據路由規則,把請求轉發給具體的微服務,微服務處理完成後,把結果返回給“routing” filter,“routing” filter 會對結果有一個處理或者不處理。“routing” filter 放行後到“post” filter (後置過濾器) , 如果沒有問題就返回給客戶端了,所以這裏的前置、後置是針對 “routing” filter的。
●正常流程:
請求到達首先會經過pre類型過濾器,而後到達routing類型, 進行路由,請求就到達真正的服務提供者,執行請求,返回結果後,會到達post過濾器。而後返回響應。
●異常流程:
1.整介過程中,pre或者routing過濾器出現異常, 都會直接進入error過濾器,再error處理完畢後,會將請求交給POST過濾器,最後返回給用戶。
2. 如果是error過濾器自己出現異常,最終也會進入POST過濾器,而後返回。
3. 如果是POST過濾器出現異常,會跳轉到error過濾器, 但是與pre和routing不同的時,請求不會再到達POST過濾器了。
使用場景
場景非常多:
●請求鑑權:一般放在pre類型,如果發現沒有訪問權限,直接就攔截了
●異常處理:一般會在error類型和post類型過濾器中結合來處理。
●服務調用時長統計: pre和post結合使用。
自定義過濾器
package com.baidu.filter;
import com.netflix.zuul.IZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class LoginFilters implements IZuulFilter {
public String filterType(){
//設置過濾器類型
return FilterConstants.PRE_TYPE;
}
//過濾器執行順序,當一個請求在同一個階段存在多個過濾器的時候,多個過濾器執行順序。
public int filterOrder(){
//設置過濾器順序
return FilterConstants.PRE_DECORATION_FILTER_ORDER-1;
}
public boolean shouldFilter(){
return true;
}
//過濾邏輯
public Object run() throws ZuulException{
//獲取請求上下文RequestContext:是一個域,作用範圍:從請求到達zuul一直到路由結束返回客戶端。
RequestContext context = RequestContext.getCurrentContext();
//獲取request
HttpServletRequest request = context.getRequest();
//獲取請求參數
String token = request.getParameter("access-token");
//判斷是否存在
if(StringUtils.isBlank(token)){
//不存在,未登錄,就攔截,參數是false就攔截
context.setSendZuulResponse(false);
//返回403
context.setResponseStatusCode(HttpStatus.FORBIDDEN.value());
}
return null;
}
}
這樣訪問路徑要加參數,例如:http://localhost:10010/user/user/findById/2?access-token=huijie
,否則會被攔下,出現403
負載均衡
Zuul中默認就已經集成了Ribbon負載均衡和Hystix熔斷機制。但是所有的超時策略都是走的默認值,比如熔斷超時時間只有1S,很容易就觸發了。因此建議我們手動進行配置:
Zuul的高可用
啓動多個Zuul服務,自動註冊到Eureka,形成集羣。如果是服務內部訪問,你訪問Zuul,自動負載均衡,沒問題。但是,Zuul更多是外部訪問,PC端、 移動端等。他們無法通過Eureka進行負載均衡,那麼該怎麼辦?此時,我們會使用其它的服務網關,來對Zuu進行代理。比如: Nginx、Eureka、Ribbon、Hystix、 Feign、 Zuul
spring-cloud-config:統一配置中心,自動去Git拉取最新的配置,緩存。使用Git的Webhook鉤子, 去通知配置中心,說配置發生了變化,配置中心會通過消息總線去通知所有的微服務,更新配置。
spring-cloud-bus:消息總線
Spring-cloud-stream:消息通信
spring -cloud-hystrix dashboard:容錯統計,形成圖形化界面
spring-cloud-sleuth:鏈路追蹤結合Zipkin