zoukankan      html  css  js  c++  java
  • 服务网关zuul之五:熔断

    路由熔断

    当我们的后端服务出现异常的时候,我们不希望将异常抛出给最外层,期望服务可以自动进行一降级。Zuul给我们提供了这样的支持。当某个服务出现异常时,直接返回我们预设的信息。

    如果没有配置fallback,zuul调用时超时了,

    我们通过自定义的fallback方法,并且将其指定给某个route来实现该route访问出问题的熔断处理。主要继承ZuulFallbackProvider接口来实现,ZuulFallbackProvider默认有两个方法,一个用来指明熔断拦截哪个服务,一个定制返回内容。

    public interface FallbackProvider extends ZuulFallbackProvider {
    
        /**
         * Provides a fallback response based on the cause of the failed execution.
         *
         * @param cause cause of the main method failure
         * @return the fallback response
         */
        ClientHttpResponse fallbackResponse(Throwable cause);
    }

    实现类通过实现getRoute方法,告诉Zuul它是负责哪个route定义的熔断。而fallbackResponse方法则是告诉 Zuul 断路出现时,它会提供一个什么返回值来处理请求。

    后来Spring又扩展了此类,丰富了返回方式,在返回的内容中添加了异常信息,因此最新版本建议直接继承类FallbackProvider

    我们以上面的spring-cloud-producer服务为例,定制它的熔断返回内容。

    package com.dxz.zuul.fallback;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.http.client.ClientHttpResponse;
    import org.springframework.stereotype.Component;
    
    @Component
    public class ProducerFallback implements FallbackProvider {
        private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class);
    
        // 指定要处理的 service。
        @Override
        public String getRoute() {
            return "service-producter";
        }
    
        public ClientHttpResponse fallbackResponse() {
            return new ClientHttpResponse() {
                @Override
                public HttpStatus getStatusCode() throws IOException {
                    return HttpStatus.OK;
                }
    
                @Override
                public int getRawStatusCode() throws IOException {
                    return 200;
                }
    
                @Override
                public String getStatusText() throws IOException {
                    return "OK";
                }
    
                @Override
                public void close() {
    
                }
    
                @Override
                public InputStream getBody() throws IOException {
                    return new ByteArrayInputStream("The service is unavailable.".getBytes());
                }
    
                @Override
                public HttpHeaders getHeaders() {
                    HttpHeaders headers = new HttpHeaders();
                    headers.setContentType(MediaType.APPLICATION_JSON);
                    return headers;
                }
            };
        }
    
        @Override
        public ClientHttpResponse fallbackResponse(Throwable cause) {
            if (cause != null && cause.getCause() != null) {
                String reason = cause.getCause().getMessage();
                logger.info("Excption {}", reason);
            }
            return fallbackResponse();
        }
    }

    路由转发配置:

    zuul.routes.api-test.path: /api-test/**
    zuul.routes.consuer.path: /service-consumer/**
    zuul.routes.consuer.serviceId: service-consumer
    
    zuul.routes.producter.path: /service-producter/**
    zuul.routes.producter.serviceId: service-producter

    为了便于演示,将zuul里的超时配置短些:

    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 6000
    hystrix.command.default.execution.timeout.enabled: true
    feign.hystrix.enabled: true
    spring.cloud.loadbalancer.retry.enabled: true
    ribbon.ReadTimeout: 6000
    ribbon.ConnectTimeout: 6000

    访问: http://127.0.0.1:8091/service-producter/book/getbook5/2?token=1

    当服务出现异常时,打印相关异常信息,并返回”The service is unavailable.”。

    Zuul 目前只支持服务级别的熔断,不支持具体到某个URL进行熔断。

    路由重试

    有时候因为网络或者其它原因,服务可能会暂时的不可用,这个时候我们希望可以再次对服务进行重试,Zuul也帮我们实现了此功能,需要结合Spring Retry 一起来实现。下面我们以上面的项目为例做演示。

    添加Spring Retry依赖

    首先在spring-cloud-zuul项目中添加Spring Retry依赖。

        compile 'org.springframework.retry:spring-retry:1.2.2.RELEASE'

    开启Zuul Retry

    再配置文件中配置启用Zuul Retry

    #是否开启重试功能
    zuul.retryable=true
    #对当前服务的重试次数
    ribbon.MaxAutoRetries=2
    #切换相同Server的次数
    ribbon.MaxAutoRetriesNextServer=0

    这样我们就开启了Zuul的重试功能。

    测试

    我们对service-producer进行改造,在getbook5方法中添加定时,并且在请求的一开始打印参数。

    @RestController
    @RequestMapping("/book")
    public class BookProducter {
    
        @Autowired
        private RestTemplate restTemplate;
        
        @RequestMapping(value = "/getbook5/{id}", method = RequestMethod.GET)
        public Book getbook5(@ApiParam("id编号") @PathVariable("id") Integer id) throws InterruptedException {
            System.out.println(">>>>>>>>/getbook5/" + id);
            int i = new Random().nextInt(20);
            TimeUnit.SECONDS.sleep(i);
            System.out.println("SLEEP=" + i + ">>>>>>>>/getbook5/" + id);
            if (id == 1) {
                return new Book(id, "《李自成》", 55, "姚雪垠", "人民文学出版社");
            } else if (id == 2) {
                return new Book(id, "中国文学简史", 33, "林庚", "清华大学出版社");
            }
            return new Book(id, "文学改良刍议", 33, "胡适", "无");
        }
    }

    重启 service-producter和zuul-demo项目。

    访问地址:http://127.0.0.1:8091/service-producter/book/getbook5/3?token=1,当页面返回:The service is unavailable.时查看项目service-producter后台日志如下:

    说明进行了三次的请求,也就是进行了两次的重试。这样也就验证了我们的配置信息,完成了Zuul的重试功能。

    注意

    开启重试在某些情况下是有问题的,比如当压力过大,一个实例停止响应时,路由将流量转到另一个实例,很有可能导致最终所有的实例全被压垮。说到底,断路器的其中一个作用就是防止故障或者压力扩散。用了retry,断路器就只有在该服务的所有实例都无法运作的情况下才能起作用。这种时候,断路器的形式更像是提供一种友好的错误信息,或者假装服务正常运行的假象给使用者。

    不用retry,仅使用负载均衡和熔断,就必须考虑到是否能够接受单个服务实例关闭和eureka刷新服务列表之间带来的短时间的熔断。如果可以接受,就无需使用retry。

  • 相关阅读:
    26. Remove Duplicates from Sorted Array(js)
    25. Reverse Nodes in k-Group(js)
    24. Swap Nodes in Pairs(js)
    23. Merge k Sorted Lists(js)
    22. Generate Parentheses(js)
    21. Merge Two Sorted Lists(js)
    20. Valid Parentheses(js)
    19. Remove Nth Node From End of List(js)
    18. 4Sum(js)
    17. Letter Combinations of a Phone Number(js)
  • 原文地址:https://www.cnblogs.com/duanxz/p/9715509.html
Copyright © 2011-2022 走看看