功能区重试属性未得到遵守

时间:2018-02-02 13:44:46

标签: netflix-zuul spring-cloud-netflix spring-retry netflix-ribbon

我有一个zuul网关应用程序,它接收来自客户端应用程序的请求,并使用负载平衡休息模板将请求转发到具有2个端点的微服务,例如endpoint1和endpoint2(循环中两个端点之间的负载平衡,现在还可以,虽然我希望它是基于可用性的)。

以下是我面临的问题 -

  • 我放下了一个端点,说端点2并尝试调用zuul路由,我看到当请求进入端点2时 - zuul需要2分钟左右才能使用HTTP 503失败并且不会重试下一个请求。错误只是级联回调用者。
  • 此外,即使设置了读取超时并连接超时配置,我也看不到关于配置的功能区,仍然需要2分钟才能从服务器中抛出错误。
  • 我尝试在netflix包级别启用日志,但除非我将自定义http客户端传递给其余模板,否则我无法看到日志。

我是netflix堆栈组件的新手......请告知我是否遗漏了一些明显的东西。感谢

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mycomp</groupId>
    <artifactId>zuul-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>zuul-gateway</name>
    <description>Spring Boot Zuul</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Edgware.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-lambda</artifactId>
            <version>1.11.242</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.3.10</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.netflix-commons</groupId>
            <artifactId>netflix-commons-util</artifactId>
            <version>0.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

我的application.yml如下所示 -

eureka:
client:
    healthcheck:
      enabled: true
    lease:
      duration: 5
    service-url:
      defaultZone: http://localhost:8761/eureka/

ingestWithOutEureka:
  ribbon:
    eureka:
      enabled: false
    NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    listOfServers: http://demo-nlb-6a67d59c901ecd128.elb.us-west-2.amazonaws.com,http://demo-nlb-124321w2a123ecd128.elb.us-west-2.amazonaws.com
    okToRetryOnAllOperations: true
    ConnectTimeout: 500
    ReadTimeout: 1000
    MaxAutoRetries: 5
    MaxAutoRetriesNextServer: 5
    MaxTotalHttpConnections: 500
    MaxConnectionsPerHost: 100
    retryableStatusCodes: 404,503
    okhttp:
      enabled: true

zuul:
  debug:
    request: true
    parameter: true
  ignored-services: '*'
  routes:
    ingestServiceELB:
      path: /ingestWithoutEureka/ingest/**
      retryable: true
      url: http://dummyURL

management.security.enabled : false

spring:
  application:
    name: zuul-gateway
  cloud:
    loadbalancer:
      retry:
        enabled: true

logging:
  level:
    org:
      apache:
        http: DEBUG
    com:
      netflix: DEBUG

hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: THREAD
          thread:
            timeoutInMilliseconds: 60000

我的应用程序类如下所示

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ZuulGatewayApplication {

    @Bean
    public InterceptionFilter addInterceptionFilter() {
        return new InterceptionFilter();
    }

    public static void main(String[] args) {
        SpringApplication.run(ZuulGatewayApplication.class, args);
    }
}

最后我的zuul过滤器如下所示 -     包com.zuulgateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.stream.Collectors;

public class InterceptionFilter extends ZuulFilter{
    private static final String REQUEST_PATH = "/ingestWithoutEureka";

    @LoadBalanced
    @Bean
    RestTemplate loadBalanced() {
        //RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate;
    }

    @Autowired
    @LoadBalanced
    private RestTemplate loadBalancedRestTemplate;

    @Override
    public String filterType() {
        return "route";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        String method = request.getMethod();
        String requestURI = request.getRequestURI();
        return requestURI.startsWith(REQUEST_PATH);
    }

    @Override
    public Object run() {

        RequestContext ctx = RequestContext.getCurrentContext();
        try {
            String requestPayload = ctx.getRequest().getReader().lines().collect(Collectors.joining(System.lineSeparator()));

            String response = loadBalancedRestTemplate.postForObject("http://ingestWithOutEureka/ingest", requestPayload, String.class);

            ctx.setResponseStatusCode(200);
            ctx.setResponseBody(response);
        } catch (IOException e) {
            ctx.setResponseStatusCode(500);
            ctx.setResponseBody("{ \"error\" : " + e.getMessage() + " }");
            System.out.println("Exception during feign call - " + e.getMessage());
            e.printStackTrace();
        } finally {
            ctx.setSendZuulResponse(false);
            ctx.getResponse().setContentType("application/json");
        }

        return null;
    }
}

1 个答案:

答案 0 :(得分:2)

所以,这里有适合我的解决方案 -

问题1 - 尽管配置ribbon.<client>.OkToRetryOnAllOperations: true,重试仍无法正常工作。 Ribbon显然忽略了我的配置。

解决方案: - 这很奇怪,但经过一些调试后,我发现只有首先出现全局配置时,才会启动Ribbon才能获取客户端级配置。

我设置全局&#34; OkToRetryOnAllOperations&#34;作为&#34; true&#34;或&#34;假&#34;如下所示,功能区开始按预期拾取ribbon.<client>.OkToRetryOnAllOperations,我可以看到重试正在进行。

ribbon:
  OkToRetryOnAllOperations: false

问题2 - 此外,即使设置了读取超时并连接超时配置,我也看不到关于配置的功能区,仍然需要2分钟才能抛出错误来自服务器

解决方案2 - 虽然功能区在上面的解决方案1中建议的更改后开始重试请求,但我没有看到功能区尊重<client>.ribbon.ReadTimeout<client>.ribbon.ConnectTimeout

花了一些时间后,我认为这是因为使用了RestTemplate

虽然spring文档提到您可以使用load balanced RestTemplate for achieving retries,但它没有提到超时不会使用它。根据此SO answer from 2014,在load balanced RestTemplate使用RestTemplate实现serviceId到URI解析时,看起来像ribbon has been added as a interceptor时,功能区不使用底层HTTP客户端并使用{{{}提供的http客户端1}}。因此,特定功能区<client>.ribbon.ReadTimeout<client>.ribbon.ConnectTimeout不受尊重。在我向RestTemplate添加超时后,请求已按预期的时间间隔开始超时。

最后, 问题3 - 我通过将自定义http客户端传递给其余模板来启用日志。

@LoadBalanced
@Bean
RestTemplate loadBalanced() {
    RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
    System.out.println("returning load balanced rest client");
    ((HttpComponentsClientHttpRequestFactory)restTemplate.getRequestFactory()).setReadTimeout(1000*30);
    ((HttpComponentsClientHttpRequestFactory)restTemplate.getRequestFactory()).setConnectTimeout(1000*3);
    ((HttpComponentsClientHttpRequestFactory)restTemplate.getRequestFactory()).setConnectionRequestTimeout(1000*3);
    return restTemplate;
}

@Bean
LoadBalancedBackOffPolicyFactory backOffPolicyFactory() {
    return new LoadBalancedBackOffPolicyFactory() {
        @Override
        public BackOffPolicy createBackOffPolicy(String service) {
            return new ExponentialBackOffPolicy();
        }
    };
}

通过所有更改,我看到请求重试正在进行,并且超时和指数退避以及请求/响应日志按预期显示。祝你好运!