白话SpringCloud | 第十章:路由网关(Zuul)进阶:过滤器、异常处理 (3)

首先,我们spring-cloud-eureka-client服务停止了,之后访问下::8889/eureka/hello?name=oKong&token=okong ,可以看见返回的就是正常boot默认异常,即:/error页面。

error

接着,访问下::8889/myapi/hello?name=oKong&token=okong ,相同的都是跳转至/error页面。

error

可以发现,第二种错误信息更加直观也更有用,可以获悉是服务不可用造成的。

现在,我们来看看,SendErrorFilter类的run方法。

sendErrorFilter

可以获悉,其主要的生效条件是包含异常对象:throwable ,而第二个条件只是为了避免二次执行。为了了解下其调用关系,我们查看下com.netflix.zuul.http.ZuulServlet类的service方法,这个类它定义了Zuul处理外部请求过程时,各个类型过滤器的执行逻辑。

ZuulServlet类service方法

以上截图了此类的service方法,可以看见,每调用一个过滤器类型时,外部都是用try..catch包裹了,异常发生时都调用了error方法,现在我们看看error()方法。

error方法

可以看见,当一个触发器发生异常时,统一设置了异常对象throwable,而后去调用error类型的过滤器。

针对网关自己的api接口时,和普通的web应用是一样的了。也是跳转至/error上,此时可以使用@ControllerAdvice进行统一异常处理。关于统一异常的处理,可以查看《SpringBoot | 第八章:统一异常、数据校验处理》,这里就不阐述了。

服务异常回退

通过前一章节,我们值得可以通过注册中心的服务ID进行自动转发,当远程服务不可用时,我们可以通过Hystrix进行服务回退处理。官网文档也说明了,只需实现FallbackProvider接口类即可。

Providing Hystrix Fallbacks For Routes

创建一个服务eureka-client的异常回退类:myEurekaClientFallback。

/** * 服务 eureka-client 的异常退回处理类 * @author oKong */ public class MyEurekaClientFallback implements FallbackProvider { @Override public String getRoute() { // TODO Auto-generated method stub return "eureka-client"; } @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) { //标记不同的异常为不同的http状态值 if (cause instanceof HystrixTimeoutException) { return response(HttpStatus.GATEWAY_TIMEOUT); } else { //可继续添加自定义异常类 return response(HttpStatus.INTERNAL_SERVER_ERROR); } } //处理 private ClientHttpResponse response(final HttpStatus status) { return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { return status; } @Override public int getRawStatusCode() throws IOException { return status.value(); } @Override public String getStatusText() throws IOException { return status.getReasonPhrase(); } @Override public void close() { } @Override public InputStream getBody() throws IOException { //可替换成相应的json串的 看业务规定了 return new ByteArrayInputStream("{\"code\":\"999999\",\"msg\":\"服务暂时不可用\"}".getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }; } }

同时在启动类中使用@Bean标记,使其生效。

@Bean public MyEurekaClientFallback eurekaClientFallback() { return new MyEurekaClientFallback(); }

此时,我们停止spring-cloud-eureka-client服务,访问::8889/eureka/hello?name=oKong&token=okong ,可以看见看见已经正确返回错误信息了。

服务回退

另外,需要细化异常的,可对fallbackResponse的Throwable进行异常判断的,以获取具体的异常信息,如超时、处理异常等等。而且,设置了服务回退,此时对于route过滤器而言是正常调用,未发生异常,所以也就不会调用error过滤器了。

常规http请求异常

当使用Ribbon进行服务调用时,我们可以使用FallbackProvider进行调用,而当我们常规的使用url进行转发时,我们也应该进行异常结果处理,以保持返回值一致。已经知道,发生异常时,会调用SendErrorFilter异常过滤器,对异常经常处理,同时重定向至/error中,所以,一般上我们可以自定义ErrorController类或者参照SendErrorFilter进行二次开发,对返回值进行个性化处理即可。这里简单演示下通过自定义异常过滤器进行异常处理。

/** * 自定义异常类 过滤器 直接扩展 SendErrorFilter 类 * @author oKong * */ @Slf4j public class CustomErrorFilter extends SendErrorFilter{ @Override public Object run() { //重写 run方法 try{ RequestContext ctx = RequestContext.getCurrentContext(); //直接复用异常处理类 ExceptionHolder exception = findZuulException(ctx.getThrowable()); log.info("异常信息:{}", exception.getThrowable()); //这里可对不同异常返回不同的错误码 HttpServletResponse response = ctx.getResponse(); response.getOutputStream().write(("{\"code\":\"999999\",\"msg\":\"" + exception.getErrorCause() + "\"}").getBytes()); }catch (Exception ex) { ReflectionUtils.rethrowRuntimeException(ex); } return null; } }

同时,禁用SendErrorFilter过滤器。

## 停用默认的异常处理器SendErrorFilter zuul.SendErrorFilter.error.disable=true

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzjgzx.html