一、概述
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 评论