<small id='0pNsV'></small> <noframes id='VX1lvDZGk'>

  • <tfoot id='34wrCS'></tfoot>

      <legend id='WZhjA'><style id='2OYW'><dir id='8RNw'><q id='CfbOFgGd'></q></dir></style></legend>
      <i id='FmyrxN'><tr id='yW2AFOoJ8w'><dt id='GJP8L4am'><q id='EXpijK'><span id='SJdK'><b id='FcIAD7z'><form id='ts8Q1'><ins id='xjlSzbEPc'></ins><ul id='kDYStm'></ul><sub id='s36zpE541'></sub></form><legend id='GavCs4nMl1'></legend><bdo id='Ycjde0Var'><pre id='oGH2i3zI5'><center id='8ptivn'></center></pre></bdo></b><th id='7HiOLBr'></th></span></q></dt></tr></i><div id='OjJmLxTGfy'><tfoot id='4fRy0EB'></tfoot><dl id='HDku97xE'><fieldset id='8NO9LJ0'></fieldset></dl></div>

          <bdo id='nNXl'></bdo><ul id='qQ3tcr'></ul>

          1. <li id='vuAp6ET'></li>
            登陆

            Spring Cloud Gateway-自定义反常处理

            admin 2019-05-18 370人围观 ,发现0个评论

            条件

            咱们平常在用SpringMVC的时分,只需是经过DispatcherServlet处理的恳求,能够经过@ControllerAdvice和@ExceptionHandler自定义不同类型反常的处理逻辑,详细能够参阅ResponseEntityExceptionHandler和DefaultHandlerExceptionResolver,底层原理很简单,便是发作反常的时分查找容器中现已存在的反常处理器而且匹配对应的反常类型,匹配成功之后运用该指定的反常处理器回来成果进行Response的烘托,假如找不到默许的反常处理器则用默许的进行兜底(个人以为,Spring在许多功用设计的时分都有这种“有则运用自定义,无则运用默许供给”这种思维非常高雅)。

            SpringMVC中供给的自定义反常体系在Spring-WebFlux中并不适用,其实原因很简单,两者底层的运转容器并不相同。WebExceptionHandler是Spring-WebFlux的反常处理器顶层接口,因而追溯到子类能够追寻到DefaultErrorWebExceptionHandler是Spring Cloud Gateway的大局反常处理器,装备类是ErrorWebFluxAutoConfiguration。

            为什么要自定义反常处理

            先画一个设想可是靠近实践架构图,定位一下网关的效果:

            网关在整个架构中的效果是:

            1. 路由服务端使用的恳求到后端使用。
            2. (聚合)后端使用的呼应转发到服务端使用。

            假定网关服务总是正常的条件下:

            关于第1点来说,假定后端使用不能滑润无损上线,会有必定的几率呈现网关路由恳求到一些后端的“僵尸节点(恳求路由曩昔的时分,使用更好在重启或许刚好中止)”,这个时分会路由会失利抛出反常,一般状况是Connection Refuse。

            关于第2点来说,假定后端使用没有正确处理反常,那么应该会把反常信息经过网关转发回到服务端使用,这种状况理论上不会呈现反常。

            其实还有第3点躲藏的问题,网关假如不单单承当路由的功用,还包含了鉴权、限流等功用,假如这些功用开发的时分对反常捕获没有做完善的处理乃至是逻辑本身存在BUG,有或许导致反常没有被正常捕获处理,走了默许的反常处理器DefaultErrorWebExceptionHandler,默许的反常处理器的处理逻辑或许并不契合咱们预期的成果。

            怎么自定义反常处理

            咱们能够先看默许的反常处理器的装备类ErrorWebFluxAutoConfiguration:

            @Configuration
            @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
            @ConditionalOnClass(WebFluxConfigurer.class)
            @AutoConfigureBefore(WebFluxAutoConfiguration.class)
            @EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class })
            public class ErrorWebFluxAutoConfiguration {
            private final ServerProperties serverProperties;
            private final ApplicationContext applicationContext;
            private final ResourceProperties resourceProperties;
            private final List viewResolvers;
            private final ServerCodecConfigurer serverCodecConfigurer;
            public ErrorWebFluxAutoConfiguration(ServerProperties serverProperties,
            ResourceProperties resourceProperties,
            ObjectProvider viewResolversProvider,
            ServerCodecConfigurer serverCodecConfigurer,
            ApplicationContext applicationContext) {
            this.serverProperties = serverProperties;
            this.applicationContext = applicationContext;
            this.resourceProperties = resourceProperties;
            this.viewResolvers = viewResolversProvider.orderedStream()
            .collect(Collectors.toList());
            this.serverCodecConfigurer = serverCodecConfigurer;
            }
            @Bean
            @ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class,
            search = SearchStrategy.CURRENT)
            @Order(-1)
            public ErrorWebExceptionHandler errorWebExceptionHandler(
            ErrorAttributes errorAttributes) {
            DefaultErrorWebExceptionHandler exceptionHandler = new DefaultErrorWebExceptionHandler(
            errorAttributes, this.resourceProperties,
            this.serverProperties.getError(), this.applicationContext);
            exceptionHandler.setViewResolvers(this.viewResolvers);
            exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
            exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
            return exceptionHandler;
            }
            @Bean
            @ConditionalOnMissingBean(value = ErrSpring Cloud Gateway-自定义反常处理orAttributes.class,
            search = SearchStrategy.CURRENT)
            public DefaultErrorAttributes errorAttributes() {
            return new DefaultErrorAttributes(
            this.serverProperties.getError().isIncludeException());
            }
            }

            注意到两个Bean实例ErrorWebExceptionHandler和DefaultErrorAttributes都运用了@ConditionalOnMissingBean注解,也便是咱们能够经过自定义完结去掩盖它们。先自定义一个CustomErrorWebFluxAutoConfiguration(除了ErrorWebExceptionHandler的自定义完结,其他直接复制ErrorWebFluxAutoConfiguration):

            @Configuration
            @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
            @ConditionalOnClass(WebFluxConfigurer.class)
            @AutoConfigureBefore(WebFluxAutoConfiguration.class)
            @EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
            public class CustomErrorWebFluxAutoConfiguration {
            private final ServerProperties serverProperties;
            private final ApplicationContext applicationContext;
            private final ResourceProperties resourceProperties;
            private final List viewResolvers;
            private final ServerCodecConfigurer serverCodecConfigurer;
            public CustomErrorWebFluxAutoConfiguration(ServerProperties serv风起苍岚漫画erProperties,
            ResourceProperties resourceProperties,
            ObjectProvider viewResolversProvider,
            ServerCodecConfigurer serverCodecConfigurer,
            ApplicationContext applicationContext) {
            this.serverProperties = serverProperties;
            this.applicationContext = applicationContext;
            this.resourceProperties = resourceProperties;
            this.viewResolvers = viewResolversProvider.orderedStream()
            .collect(Collectors.toList());
            this.serverCodecConfigurer = serverCodecConfigurer;
            }
            @Bean
            @ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class,
            search = SearchStrategy.CURRENT)
            @Order(-1)
            public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
            // TODO 这儿完结自定义ErrorWebExceptionHandler完结逻辑
            return null;
            }
            @Bean
            @ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
            public DefaultErrorAttributes errorAttributes() {
            return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
            }
            }

            ErrorWebExceptionHandler的完结,能够直接参阅DefaultErrorWebExceptionHandler,乃至直接承继DefaultErrorWebExceptionHandler,掩盖对应的办法即可。这儿直接把反常信息封装成下面格局的Response回来,最终需求烘托成JSON格局:

            {
            "code": 200,
            "message": "描绘信息",
            "path" : "恳求途径",
            "method": "恳求办法"
            }

            咱们需求剖析一下DefaultErrorWebExceptionHandler中的一些源码:

            // 封装反常特点
            protected Map getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
            return this.errorAttributes.getErrorAttributes(request, includeStackTrace);
            }
            // 烘托反常Response
            protected Mono renderErrorResponse(ServerRequest request) {
            boolean includeStackTrace = isIncludeStackTrace(request, MediaType.ALL);
            Map error = getErrorAttributes(request, includeStackTrace);
            return ServerResponse.status(getHttpStatus(error))
            .contentType(MediaType.APPLICATION_JSON_UTF8)
            .body(BodyInsSpring Cloud Gateway-自定义反常处理erters.fromObject(error));
            }
            // 回来路由办法依据ServerResponse的目标
            @Override
            protected RouterFunction getRoutingFunction(ErrorAttributes errorAttributes) {
            return route(acceptsTextHtml(), this::renderErrorView).andRoute(all(), this::renderErrorResponse);
            }
            // HTTP呼应状况码的封装,本来是依据反常特点的status特点进行解析的
            protected HttpStatus getHttpStatus(Map errorAttributes) {
            int statusCode = (int) errorAttributes.get("status");
            return HttpStatus.valueOf(statusCode);
            }

            确认三点:

            1. 最终封装到呼应体的目标来源于DefaultErrorWebExceptionHandler#getErrorAttributes(),而且成果是一个Map实例转换成的字节序列。
            2. 本来的RouterFunction完结只支撑HTML格局回来,咱们需求修改为JSON格局回来(或许说支撑一切格局回来)。
            3. DefaultErrorWebExceptionHandler#getHttpStatus()是呼应状况码的封装,本来的逻辑是依据反常特点getErrorAttributes()的status特点进行解析的。

            自定义的JsonErrorWebExceptionHandler如下:

            public class JsonErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {
            public JsonErrorWebExceptionHandler(ErrorAttributes errorAttributes,
            ResourceProperties resourceProperties,
            ErrorProperties errorPropertiSpring Cloud Gateway-自定义反常处理es,
            ApplicationContext applicationContext) {
            super(errorAttributes, resourceProperties, errorProperties, applicationContext);
            }
            @Override
            protected Map getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
            // 这儿其实能够依据反常类型进行定制化逻辑
            Throwable error = super.getError(request);
            Map errorAttributes = new HashMap<>(8);
            errorAttributes.put("message", error.getMessage());
            errorAttributes.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
            errorAttributes.put("method", request.methodName());
            errorAttributes.put("path", request.path());
            return errorAttributes;
            }
            @Override
            protected RouterFunction getRoutingFunction(ErrorAttributes errorAttributes) {
            return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
            }
            @Override
            protected HttpStatus getHttpStatus(Map errorAttributes) {
            // 这儿其实能够依据errorAttributes里边的特点定制HTTP呼应码
            return HttpStatus.INTERNAL_SERVER_ERROR;
            }
            }

            装备类CustomErrorWebFluxAutoConfiguration增加JsonErrorWebExceptionHandler:

            @Bean
            @ConditionalOnMissingBean(value = ErrorWebExceptionHandler.class, search = SearchStrategy.CURRENT)
            @Order(-1)
            public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
            JsonErrorWebExceptionHandler exceptionHandler = new JsonErrorWebExceptionHandler(
            errorAttributes,
            resourceProperties,
            this.serverProperties.getError(),
            applicationContext);
            exceptionHandler.setViewResolvers(this.viewResolvers);
            exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
            exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
            return exceptionHandler;
            }

            很简单,这儿把反常的HTTP呼应状况码共同为HttpStatus.INTERNAL_SERVER_ERROR(500),改造的东西并不多,只需了解本来反常处理的上下文逻辑即可。

            测验

            测验场景一:只发动网关,下流服务不发动的状况下直接调用下流服务:

            curl http://localhost:9090/order/host
            // 呼应成果
            {"path":"/order/host","code":500,"message":"Connection refused: no further information: localhost/127.0.0.1:9091","method":"GET"}

            测验场景二:下流服务正常发动和调用,网关本身抛出反常。

            在网关使用自定义一个大局过滤器而且成心抛出反常:

            @Component
            public class ErrorGlobalFilter implements GlobalFilter {
            @Override
            public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            int i = 1/0;
            return chain.filter(exchange);
            }
            }
            curl http://localhost:9090/order/host
            // 呼应成果
            {"path":"/order/host","code":500,"message":"/ by zero","method":"GET"}

            呼应成果和定制的逻辑共同,而且后台的日志也打印了对应的反常仓库。

            小结

            笔者一向以为,做反常分类和依照分类处理是工程里边非常重要的一环。笔者在地点公司担任的体系中,坚持完结反常分类捕获,主要是需求区别能够重试补偿以及无法重试需求及时预警的反常,这姿态才干针对可康复反常定制自愈逻辑,对不能康复的反常及时预警和人为介入。所以,Spring Cloud Gateway这个技能栈也有必要调研其自定义反常的处理逻辑。

            请关注微信公众号
            微信二维码
            不容错过
            Powered By Z-BlogPHP