Monday, February 13, 2017

Generic ZuulFallbackProvider for Spring Cloud Netflix Zuul Proxy



Spring Cloud Netflix provides easy way of routing using Zuul proxy. It has a feature of circuit breaker in case of heavy load. Though underlying Hystrix provides a fallback, till recently Zuul didn't provide same. Zuul implemented this functionality in Camden SR2. Documentation for using Zuul Fallback is available here.

If you have multiple Zuul routes in your application, it will be cumbersome to write multiple fallback providers. GenericZuulFallbackProvider.java below can be used to provide a generic fallback  in your application.  Then you need to provide a bean for each route returning this generic fallback after setting route (and other fields like status code, response body etc).  Example of beans are available below in ZuulProxyApplication.java.

There is pending enhancement to provide default Zuul fallback. Below code might be still useful for cases where you need to override only some fields.
## Part of application.yml
zuul:
routes:
route1:
path: /route1/**
strip-prefix: false
route2:
path: /route2/**
strip-prefix: false
view raw application.yml hosted with ❤ by GitHub
package com.example.Zullfallback;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
public class GenericZuulFallbackProvider implements ZuulFallbackProvider {
private String responseBody = "{\"message\":\"Service Unavailable. Please try after sometime\"}";
private HttpHeaders headers = null;
private String route = null;
private int rawStatusCode = 503;
private HttpStatus statusCode = HttpStatus.SERVICE_UNAVAILABLE;
private String statusText = "Service Unavailable";
public String getStatusText() {
return statusText;
}
public void setStatusText(String statusText) {
this.statusText = statusText;
}
public HttpHeaders getHeaders() {
return headers;
}
public int getRawStatusCode() {
return rawStatusCode;
}
public HttpStatus getStatusCode() {
return statusCode;
}
public void setRawStatusCode(int rawStatusCode) {
this.rawStatusCode = rawStatusCode;
}
public void setStatusCode(HttpStatus statusCode) {
this.statusCode = statusCode;
}
public void setHeaders(HttpHeaders headers) {
this.headers = headers;
}
public void setRoute(String route) {
this.route = route;
}
public String getResponseBody() {
return responseBody;
}
public void setResponseBody(String responseBody) {
this.responseBody = responseBody;
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse(){
@Override
public InputStream getBody() throws IOException {
if (responseBody == null)
responseBody ="{\"message\":\"Service Unavailable. Please try after sometime\"}";
return new ByteArrayInputStream(responseBody.getBytes());
}
@Override
public HttpHeaders getHeaders() {
if (headers == null) {
headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
}
return headers;
}
@Override
public void close() {
}
@Override
public int getRawStatusCode() throws IOException {
return rawStatusCode;
}
@Override
public HttpStatus getStatusCode() throws IOException {
if (statusCode == null)
statusCode = HttpStatus.SERVICE_UNAVAILABLE;
return statusCode;
}
@Override
public String getStatusText() throws IOException {
if (statusText == null)
statusText = "Service Unavailable";
return statusText;
}
};
}
@Override
public String getRoute() {
if (route == null)
route = "route";
return route;
}
}
package com.example.ZuulProxy;
import java.io.File;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
import com.example.Zullfallback;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
@SpringBootApplication
@Controller
@EnableZuulProxy
public class ZuulProxyApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ZuulProxyApplication.class).web(true).run(args);
}
@Bean
public ZuulFallbackProvider route1ZuulFallbackProvider() {
GenericZuulFallbackProvider route1ZuulFallback = new GenericZuulFallbackProvider();
route1ZuulFallback.setRoute("route1");
return route1ZuulFallback;
}
@Bean
public ZuulFallbackProvider route2ZuulFallbackProvider() {
GenericZuulFallbackProvider route2ZullFallback = new GenericZuulFallbackProvider();
route2ZullFallback.setRoute("route2");
route2ZuulFallback.setRawStatusCode(200);
route2ZuulFallback.setStatusCode(HttpStatus.OK);
route2ZuulFallback.setResponseBody("We are little busy. Comeback After Sometime");
return portalZullFallback;
}
}