Ricardo Piccoli
Ricardo Piccoli

Reputation: 491

Spring Cloud Zuul fallback service

Is it possible to setup a proxy to a main server/servers/service but when this is not available, redirect requests to a secondary server/servers/service?

The secondary service is not an instance of the same type of the primary one. Their API is compatible but not the same. The secondary is a less-performant last resource when the primary is not available.

We are not using Eureka, only fixed IPs yet.

zuul:
  routes:
    whatever:
      path: /whatever/**
      sensitiveHeaders: Cookie,Set-Cookie
      url: http://server1:8080/whatever

I had a look on ZuulFallbackProvider, but this interface is to provided a fixed response on case of error. I want, when http://server1:8080/whatever is not responding, redirect to http://server2:8080/whateverApi2.

Thanks.

Upvotes: 4

Views: 2814

Answers (3)

dos4dev
dos4dev

Reputation: 533

Need add retryable

zuul:
  routes:
    whatever:
      path: /whatever/**
      sensitiveHeaders: Cookie,Set-Cookie
      url: http://server1:8080/whatever
      retryable=true

Similar situation

Upvotes: 0

Ricardo Piccoli
Ricardo Piccoli

Reputation: 491

If anyone is trying to do something similar, what actually works is Hystrix instead of Zuul component.

In the Gateway API, we create a facade controller that respond to the service we want to set the fallback solution:

@HystrixCommand(fallbackMethod = "fallbackWhatever")
@PostMapping
ResponseEntity<Object> whatever(final RequestEntity<?> request) {
    return defaultClient.searchSubmissions(request.getHeaders(), request.getBody());
}

ResponseEntity<Object> fallbackWhatever(final RequestEntity<?> request) {
    return fallbackClient.searchSubmissions(request.getHeaders(), request.getBody());
}

defaultClient and fallbackClient are two different FeignClient interfaces, each one pointing to each service endpoint.

That's it! If you shutdown the main service, the Gateway API starts to call the fallback service without ever returning a single service unavailable and the change takes just a few milliseconds.

Also, if you turn it back on, it's back responding also just a few milliseconds after being ready.

Upvotes: 4

yongsung.yoon
yongsung.yoon

Reputation: 5589

You can do that with ZuulFallbackProvider, but you need to configure the below first.

First, url-routing - specifying url directly in zuul.routes.<service>.url - in Zuul is NOT executed in HystrixCommand. To achieve this, you need to change your configuration like the below.

zuul:
  routes:
    whatever:
      path: /whatever/**
      sensitiveHeaders: Cookie,Set-Cookie
      serviceId: whatever
      stripPrefix: false

ribbon:
  eureka:
    enabled: false

whatever:
  ribbon:
    listOfServers: http://server1:8080/

The above configuration is using Ribbon without eureka. You can find the detais here

Now, your request will be executed in HystrixCommand via Ribbon. So you can provide your own ZuulFallbackProvider.

In ZuulFallbackProvider, you can make your fallback request to your http://server2:8080/whateverApi2. in fallbackResponse method like below. The below is a very naive example. :-) You need to complete the below example for your own purpose.

@Component
public class TestZuulFallbackProvider implements ZuulFallbackProvider{
    @Override
    public String getRoute() {
        return "test";
    }

    @Override
    public ClientHttpResponse fallbackResponse() {

        ResponseEntity<String> response = new RestTemplate().exchange("http://server2:8080/whateverApi2", HttpMethod.GET, null, String.class);
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return response.getStatusCode();
            }
            @Override
            public int getRawStatusCode() throws IOException {
                return response.getStatusCodeValue();
            }
            @Override
            public String getStatusText() throws IOException {
                return response.getStatusCode().getReasonPhrase();
            }
            @Override
            public void close() {
            }
            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream(response.getBody().getBytes("UTF-8"));
            }
            @Override
            public HttpHeaders getHeaders() {
                return response.getHeaders();
            }
        };
    }
}

Upvotes: 6

Related Questions