什麼是API網關?
在微服務架構中,通常會将業務子產品化,細分為多個服務,譬如:訂單服務、檔案服務、社交服務等。随着業務體系的逐漸龐大,微服務也會增加,在多個微服務中提供統一的通路入口,在依據API路由到服務提供者,運用網關就可以對外暴露聚合API,屏蔽内部微服務的具體實施者,保持整個系統的穩定性。當然這隻是網關衆多功能中的一部分,除此之外,還可以做負載均衡,統一鑒權,協定轉換,監控監測等一系列功能。
什麼是Zuul?
Zuul是Spring Cloud五大元件之一的微服務API網關。
裝置或網站的請求,經過API網關Zuul過濾,轉發到具體的服務提供方。
Zuul特點:
- 認證和安全,識别每個需要認證的資源,拒絕非法請求;
- 性能監測,在服務邊界追蹤并統計資料,提供精确的生産視圖;
- 動态路由,根據API資源路徑,轉發到具體的服務提供者;
- IP限流,自定義同一IP在規定時間内通路系統的次數;
- 負載均衡, 無縫銜接ribbon、Hystrix,提升系統的高可用性;
- 減少用戶端與服務端的耦合,服務可以獨立發展,通過網關層來做映射;
Zuul網關過濾器
Zuul中提供了過濾器定義,可以用來過濾代理請求,提供額外功能邏輯。如:權限驗證,日志記錄等。
Zuul提供的過濾器是一個父類。父類是ZuulFilter提供了4個抽象方法,分别是:filterType, filterOrder, shouldFilter, run。
filterType:方法傳回字元串資料,代表目前過濾器的類型。可選值有pre、route、post、error,其功能分别是:
- pre:前置過濾,是請求進入Zuul之後,立刻執行的過濾邏輯。
- routed:路由後過濾,是請求進入Zuul之後,并Zuul實作了請求路由後執行的過濾邏輯,路由後過濾,是在遠端服務調用之前過濾的邏輯。
- post:後置過濾,遠端服務調用結束後執行的過濾邏輯。
- error:異常過濾,是任意一個過濾器發生異常或遠端服務調用無結果回報的時候執行的過濾邏輯。無結果回報,就是遠端服務調用逾時。
filterOrder:傳回int資料,用于為同filterType的多個過濾器定制執行順序,傳回值越小,執行順序越優先;
shouldFilter:傳回boolean資料,代表目前filter是否生效;
run:具體的過濾執行邏輯。如pre類型的過濾器,可以通過對請求的驗證來決定是否将請求路由到服務上;如post類型的過濾器,可以對服務響應結果做加工處理(如為每個響應增加footer資料)。
Zuul過濾器 的生命周期
代碼示例
/**
* Zuul過濾器,必須繼承ZuulFilter父類。
* 目前類型的對象必須交由Spring容器管理。使用@Component注解描述。
* 繼承父類後,必須實作父類中定義的4個抽象方法。
* shouldFilter、 run、 filterType、 filterOrder
*/
@Component
public class LoggerFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(LoggerFilter.class);
/**
* 傳回boolean類型。代表目前filter是否生效。
* 預設值為false。
* 傳回true代表開啟filter。
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* run方法就是過濾器的具體邏輯。
* return 可以傳回任意的對象,目前實作忽略。(spring-cloud-zuul官方解釋)
* 直接傳回null即可。
*/
@Override
public Object run() throws ZuulException {
// 通過zuul,擷取請求上下文
RequestContext rc = RequestContext.getCurrentContext();
HttpServletRequest request = rc.getRequest();
logger.info("LogFilter1.....method={},url={}",
request.getMethod(),request.getRequestURL().toString());
// 可以記錄日志、鑒權,給維護人員記錄提供定位協助、統計性能
return null;
}
/**
* 過濾器的類型。可選值有:
* pre - 前置過濾
* route - 路由後過濾
* error - 異常過濾
* post - 遠端服務調用後過濾
*/
@Override
public String filterType() {
return "pre";
}
/**
* 同種類的過濾器的執行順序。
* 按照傳回值的自然升序執行。
*/
@Override
public int filterOrder() {
return 0;
}
}
Zuul網關容錯(與hystrix無縫銜接)
在spring cloud中,Zuul啟動器中包含了Hystrix相關依賴,在Zuul網關工程中,預設是提供了Hystrix Dashboard服務監控資料的(hystrix.stream),但是不會提供監控面闆的界面展示。可以說,在spring cloud中,zuul和Hystrix是無縫結合的。
- Zuul中的服務降級處理
在Edgware版本之前,Zuul提供了接口ZuulFallbackProvider用于實作fallback處理。從Edgware版本開始,Zuul提供了ZuulFallbackProvider的子接口FallbackProvider來提供fallback處理。
Zuul的fallback容錯處理邏輯,隻針對timeout異常處理,當請求被Zuul路由後,隻要服務有傳回(包括異常),都不會觸發Zuul的fallback容錯邏輯。
因為對于Zuul網關來說,做請求路由分發的時候,結果由遠端服務運算的。那麼遠端服務回報了異常資訊,Zuul網關不會處理異常,因為無法确定這個錯誤是否是應用真實想要回報給用戶端的。
- 代碼示例
@Component
public class DefaultFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
//比對微服務名字,*表示比對所有
return "*";
}
@Override
public ClientHttpResponse fallbackResponse() {
return null;
}
@Override
public ClientHttpResponse fallbackResponse(Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
//當fallback時傳回給調用者的狀态碼
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return this.getStatusCode().value();
}
@Override
public String getStatusText() throws IOException {
//狀态碼的文本形式
return null;
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
//響應體
return new ByteArrayInputStream("default fallback".getBytes());
}
@Override
public HttpHeaders getHeaders() {
//設定headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_HTML);
return headers;
}
};
}
}
Zuul網關限流政策
Zuul網關元件也提供了限流保護。當請求并發達到閥值,自動觸發限流保護,傳回錯誤結果。隻要提供error錯誤處理機制即可。
Zuul的限流保護需要額外依賴spring-cloud-zuul-ratelimit元件。
- 全局限流配置
# 開啟限流保護
zuul.ratelimit.enabled=true
# 60s内請求超過3次,服務端就抛出異常,60s後可以恢複正常請求
zuul.ratelimit.default-policy.limit=3
zuul.ratelimit.default-policy.refresh-interval=60
# 針對IP進行限流,不影響其他IP
zuul.ratelimit.default-policy.type=origin
- 局部限流配置
# 開啟限流保護
zuul.ratelimit.enabled=true
# hystrix-application-client服務60s内請求超過3次,服務抛出異常。
zuul.ratelimit.policies.hystrix-application-client.limit=3
zuul.ratelimit.policies.hystrix-application-client.refresh-interval=60
# 針對IP限流。
zuul.ratelimit.policies.hystrix-application-client.type=origin
- 限流參數
Zuul網關請求逾時
使用Zuul的spring cloud微服務結構圖:
從上圖中可以看出。整體請求邏輯還是比較複雜的,在沒有zuul網關的情況下,app client請求app service的時候,也有請求逾時的可能。那麼當增加了zuul網關的時候,請求逾時的可能就更明顯了。
當請求通過zuul網關路由到服務,并等待服務傳回響應,這個過程中zuul也有逾時控制。zuul的底層使用的是Hystrix+ribbon來實作請求路由。結構如下:
zuul中的Hystrix内部使用線程池隔離機制提供請求路由實作,其預設的逾時時長為1000毫秒。ribbon底層預設逾時時長為5000毫秒。如果Hystrix逾時,直接傳回逾時異常。如果ribbon逾時,同時Hystrix未逾時,ribbon會自動進行服務叢集輪詢重試,直到Hystrix逾時為止。如果Hystrix逾時時長小于ribbon逾時時長,ribbon不會進行服務叢集輪詢重試。
那麼在zuul中可配置的逾時時長就有兩個位置:Hystrix和ribbon。具體配置如下:
# 開啟zuul網關重試
zuul.retryable=true
# hystrix逾時時間設定
# 線程池隔離,預設逾時時間1000ms
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=8000
# ribbon逾時時間設定:建議設定比Hystrix小
# 請求連接配接的逾時時間: 預設5000ms
ribbon.ConnectTimeout=5000
# 請求處理的逾時時間: 預設5000ms
ribbon.ReadTimeout=5000
# 重試次數:MaxAutoRetries表示通路服務叢集下原節點(同路徑通路);MaxAutoRetriesNextServer表示通路服務叢集下其餘節點(換台伺服器)
ribbon.MaxAutoRetries=1
ribbon.MaxAutoRetriesNextServer=1
# 開啟重試
ribbon.OkToRetryOnAllOperations=true
Spring-cloud中的zuul網關重試機制是使用spring-retry實作的。工程必須依賴下述資源:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
Zuul網關高可用
Zuul網關元件的高可用,關系到整個系統的可通路性,外部請求路由到具體服務者,全會經過Zuul,為避免單點故障,網關的高可用非常關鍵。
方案一:多節點Zuul注冊到Eureka Server上,即可實作Zuul的高可用
方案二:Zuul用戶端未注冊到Eureka Server
例如,Zuul用戶端是一個手機APP——我們不可能讓所有的手機終端都注冊到Eureka Server上,可借助一個額外的負載均衡器來實作Zuul的高可用,例如Nginx、HAProxy、F5等。