功能区重试属性不受尊重 [英] Ribbon Retry properties not respected

查看:121
本文介绍了功能区重试属性不受尊重的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个zuul网关应用程序,该应用程序从客户端应用程序接收请求,并使用负载平衡的剩余模板将请求转发到具有2个端点(例如,端点1和端点2)的微服务(轮循机制中两个端点之间的负载平衡),目前还可以,尽管我希望它基于可用性).

I have a zuul gateway application, that receives requests from a client app and forwards the requests using load balanced rest template to a micro service with 2 endpoints, say endpoint1 and endpoint2 (load balancing between the two end points in round robbin which is okay for now, although I want it to be availability based).

这是我面临的问题-

  • 我关闭了端点2之一,例如endpoint2,并尝试调用了zuul路由,我发现当请求发送到端点2时-zuul需要2分钟左右的时间,然后HTTP 503才会失败,并且不会重试下一个要求.错误只是级联返回给调用者.
  • 即使设置了读取超时和连接超时配置之后,我仍然看不到功能区遵守配置,并且仍然需要2分钟才能从服务器抛出错误.
  • 我尝试在netflix软件包级别启用日志,但是除非将自定义的http客户端传递给rest模板,否则我将看不到日志.

我是netflix组件堆栈的新手...如果我缺少明显的内容,请告知.谢谢

I am new to netflix stack of components... please advise if I am missing something obvious. Thanks

<?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如下所示-

and my application.yml looks like below -

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

我的应用程序类如下所示

and my application class looks like below

@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;

and lastly my zuul filter looks like below - package 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;
    }
}

推荐答案

因此,以下是对我有用的解决方案-

So, here are the solutions that worked for me -

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

Issue 1 - Retry was not working in spite of configuring ribbon.<client>.OkToRetryOnAllOperations: true. Ribbon was clearly ignoring my configuration.

解决方案:-很奇怪,但是经过一些调试后,我注意到Ribbon只能在首先存在全局配置的情况下选择客户端级别的配置.

Solution: - It's strange, but after some debugging I had noticed that Ribbon was picking up client level configuration only if a global configuration was present in the first place.

一旦将全局"OkToRetryOnAllOperations"设置为"true"或"false"(如下所示),功能区开始按预期启动ribbon.<client>.OkToRetryOnAllOperations,我可以看到重试发生.

Once I set the global "OkToRetryOnAllOperations" as either "true" or "false" as shown below, ribbon started picking up the ribbon.<client>.OkToRetryOnAllOperations as expected and I could see the retries happening.

ribbon:
  OkToRetryOnAllOperations: false

问题2 -此外,即使设置了读取超时和连接超时配置,我也看不到功能区遵守该配置,并且仍然需要2分钟的时间才能从服务器

解决方案2 -尽管功能区在上述解决方案1中建议的更改之后开始重试请求,但我没有看到功能区遵循<client>.ribbon.ReadTimeout<client>.ribbon.ConnectTimeout.

Solution 2 - Though ribbon started retrying requests after the changes suggested in solution 1 above, I did not see ribbon honoring <client>.ribbon.ReadTimeout and <client>.ribbon.ConnectTimeout.

花了一些时间后,我认为这是由于使用RestTemplate造成的.

After spending some time, I figure that this is because of using RestTemplate.

虽然spring文档提到您可以使用load balanced RestTemplate SO答案

While spring documentation mentions that you could use load balanced RestTemplate for achieving retries, it does not mention that the timeouts wont work with it. Based on this SO answer from 2014, it looks like while ribbon has been added as a interceptor when using load balanced RestTemplate to achieve serviceId to URI resolution, ribbon does not use the underlying HTTP client and uses the http client provided by the RestTemplate. Thus, the ribbon specific <client>.ribbon.ReadTimeout and <client>.ribbon.ConnectTimeout are NOT honored. After I added timeouts to RestTemplate, requests have started timing out at expected intervals.

最后, 问题3 -我通过将自定义的http客户端传递到rest模板来启用日志.

Lastly, Issue 3 - I enabled logs by passing a custom http client to rest template.

@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();
        }
    };
}

通过所有更改,我看到请求重试正在发生,超时,指数回退以及可见的请求/响应日志.祝你好运!

With all the changes I see that the request retries are happening and with the timeouts and with exponential backoff and with request / responses logs visible as expected. Good luck!

这篇关于功能区重试属性不受尊重的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆