1. 概述
在本教程中,我們將探討 Netflix Zuul 的後置過濾器。
Netflix Zuul 是一個位於 API 客户端和眾多微服務的邊緣服務提供者。
後置過濾器在最終響應發送到 API 客户端之前運行。這使我們有機會處理原始響應體並執行我們希望的任何操作,例如日誌記錄和其他數據轉換。
2. 依賴項
我們將要在 Spring Cloud 環境中使用 Zuul。因此,我們需要在我們的 pom.xml 的依賴管理部分添加以下內容:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
</dependencies>最新版本的 Spring Cloud 依賴項 和 spring-cloud-starter-netflix-zuul 可在 Maven Central 上找到。
3. 創建一個發佈過濾
發佈過濾是一個繼承抽象類 ZuulFilter 的常規類,並且具有 post 類型的過濾:
public class ResponseLogFilter extends ZuulFilter {
@Override
public String filterType() {
return POST_TYPE;
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
return null;
}
}請注意,我們在 filterType() 方法中返回了 POST_TYPE。 這正是將此過濾器與其他類型區分開來的關鍵。
另一個重要的方法是 shouldFilter() 方法。 我們在這裏返回 true,因為我們希望過濾器在過濾器鏈中運行。
在生產環境中,為了更好的靈活性,我們可以將此配置外部化。
讓我們更詳細地查看 run() 方法,該方法在我們的過濾器運行時會被調用。
4. 修改響應體
如前所述,Zuul 位於微服務和客户端之間。因此,它可以訪問響應體並根據需要對其進行修改後再傳遞給下游服務。
例如,我們可以讀取響應體並記錄其內容:
@Override
public Object run() throws ZuulException {
RequestContext context = RequestContext.getCurrentContext();
try (final InputStream responseDataStream = context.getResponseDataStream()) {
if(responseDataStream == null) {
logger.info("BODY: {}", "");
return null;
}
String responseData = CharStreams.toString(new InputStreamReader(responseDataStream, "UTF-8"));
logger.info("BODY: {}", responseData);
context.setResponseBody(responseData);
}
catch (Exception e) {
throw new ZuulException(e, INTERNAL_SERVER_ERROR.value(), e.getMessage());
}
return null;
}上面的片段展示了我們在先前創建的 ResponseLogFilter 中 run() 方法的完整實現。首先,我們獲取了 RequestContext 的實例。並且,通過該上下文,我們能夠使用 try-with-resources 結構獲取響應數據 InputStream。
請注意,響應輸入流可能為 null,因此我們進行了檢查。這可能是由於微服務上的服務超時或其他意外異常造成的。在我們的情況下,當發生這種情況時,我們只是記錄一個空響應體。
接下來,我們將輸入流讀取到一個 String 中,以便我們可以記錄它。
非常重要的是,我們使用 context.setResponseBody(responseData) 將響應體重新添加到上下文中以進行處理。 如果省略此步驟,我們將遇到如下的 IOException: java.io.IOException: Attempted read on a closed stream.
5. 結論
綜上所述,Zuul 中的後過濾器為開發者提供了一個在將響應發送到客户端之前,對服務響應進行操作的機會。
然而,我們必須謹慎,以免意外暴露敏感信息,從而導致安全漏洞。
此外,我們應避免在後過濾器中執行耗時任務,因為這會顯著增加響應時間。