一、概述
Zuul 是Netflix 的基于JVM 的路由器和服务器端负载均衡器。Zuul 的规则引擎提供了编写规则和过滤器的灵活性,以增强Spring Cloud 微服务架构中的路由。
在本文中,我们将通过编写自定义错误过滤器来探索如何在Zuul 中自定义异常和错误响应,这些过滤器会在代码执行期间发生错误时运行。
2. Zuul 异常
Zuul 中所有处理的异常都是ZuulExceptions。现在,让我们明确一点,ZuulException不能被@ControllerAdvice捕获并通过@ExceptionHandling注释方法。这是因为** ZuulException**是从错误过滤器中抛出的。因此,它会跳过后续的过滤器链,并且永远不会到达错误控制器。下图描述了Zuul 中错误处理的层次结构:

当出现ZuulException时,Zuul 显示如下错误响应:
{
"timestamp": "2022-01-23T22:43:43.126+00:00",
"status": 500,
"error": "Internal Server Error"
}在某些场景下,我们可能需要在ZuulException.Zuul 过滤器来救援。在下一节中,我们将讨论如何扩展Zuul 的错误过滤器和自定义ZuulException.
3. 自定义Zuul 异常
spring-cloud-starter-netflix-zuul的入门包包括三种类型的过滤器:前置、后置和错误过滤器。在这里,我们将深入了解错误过滤器并探索名为SendErrorFilter的Zuul 错误过滤器的自定义。
首先,我们将禁用自动配置的默认SendErrorFilter。这使我们不必担心执行顺序,因为这是唯一的Zuul 默认错误过滤器。让我们在application.yml中添加属性来禁用它:
zuul: SendErrorFilter: post: disable: true
现在,让我们编写一个名为CustomZuulErrorFilter的自定义Zuul 错误过滤器,如果底层服务不可用,它会抛出自定义异常:
public class CustomZuulErrorFilter extends ZuulFilter {
}这个自定义过滤器需要扩展com.netflix.zuul.ZuulFilter并覆盖它的一些方法`.`
首先,我们必须重写filterType()方法并将类型作为“error”返回。这是因为我们要为错误过滤器类型配置Zuul 过滤器:
@Override
public String filterType() {
return "error";
}之后,我们覆盖filterOrder()并返回-1,因此过滤器是链中的第一个:
@Override
public int filterOrder() {
return -1;
}然后,我们重写shouldFilter()方法并无条件返回true,因为我们希望在所有情况下都链接此过滤器:
@Override
public boolean shouldFilter() {
return true;
}最后,让我们重写run()方法:
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
Throwable throwable = context.getThrowable();
if (throwable instanceof ZuulException) {
ZuulException zuulException = (ZuulException) throwable;
if (throwable.getCause().getCause().getCause() instanceof ConnectException) {
context.remove("throwable");
context.setResponseBody(RESPONSE_BODY);
context.getResponse()
.setContentType("application/json");
context.setResponseStatusCode(503);
}
}
return null;
}让我们分解这个run()方法以了解它在做什么。首先,我们获取RequestContext的实例。接下来,我们验证从RequestContext获得的throwable是否是ZuulException的实例。然后,我们检查throwable中嵌套异常的原因是否是ConnectException的实例。最后,我们使用响应的自定义属性设置了上下文。
请注意,在设置自定义响应之前,我们会从上下文中清除throwable,以防止在后续过滤器中进行进一步的错误处理。
此外,我们还可以在run()方法中设置一个自定义异常,该异常可以由后续过滤器处理:
if (throwable.getCause().getCause().getCause() instanceof ConnectException) {
ZuulException customException = new ZuulException("", 503, "Service Unavailable");
context.setThrowable(customException);
}上面的代码片段将记录堆栈跟踪并继续执行下一个过滤器。
此外,我们可以修改这个例子来处理ZuulFilter.
4. 测试自定义Zuul 异常
在本节中,我们将在CustomZuulErrorFilter中测试自定义Zuul 异常。
假设有一个ConnectException,上述示例在Zuul API 的响应中的输出将是:
{
"timestamp": "2022-01-23T23:10:25.584791Z",
"status": 503,
"error": "Service Unavailable"
}此外,**我们始终error.path /error**在application.yml文件中配置error.path 属性来更改默认的Zuul 错误转发路径/error。
现在,让我们通过一些测试用例来验证它:
@Test
public void whenSendRequestWithCustomErrorFilter_thenCustomError() {
Response response = RestAssured.get("http://localhost:8080/foos/1");
assertEquals(503, response.getStatusCode());
}在上面的测试场景中,/foos/1的路由被故意保留下来,导致java.lang. ConnectException。结果,我们的自定义过滤器将拦截并响应503 状态。
现在,让我们在不注册自定义错误过滤器的情况下进行测试:
@Test
public void whenSendRequestWithoutCustomErrorFilter_thenError() {
Response response = RestAssured.get("http://localhost:8080/foos/1");
assertEquals(500, response.getStatusCode());
}在不注册自定义错误过滤器的情况下执行上述测试用例会导致Zuul 响应状态为500。
5. 结论
在本教程中,我们了解了错误处理的层次结构,并深入研究了在Spring Zuul 应用程序中配置自定义Zuul 错误过滤器。此错误过滤器提供了自定义响应正文和响应代码的机会。
0 评论