Core Zuul Filter Architecture
Filters serve as the fundamental building blocks in Zuul, enabling control over external service interactions. The filter lifecycle comprises four distinct phases: "PRE", "ROUTING", "POST", and "ERROR", as illustrated in the following diagram:
- PRE: Executes before request routing occurs. Used for authentication, service selection, and debug logging.
- ROUTING: Handles actual request forwarding to microservices using Apache HttpClient or Netflix Ribbon.
- POST: Processes responses after microservice invocation. Adds standard HTTP headers, collects metrics, and sends responses to clients.
- ERROR: Triggered when exceptions occur in other phases.
Default Zuul Filter Implementations
| Type | Order | Filter | Purpose |
|---|---|---|---|
| pre | -3 | ServletDetectionFilter | Identifies Servlet type |
| pre | -2 | Servlet30WrapperFilter | Wraps HttpServletRequest |
| pre | -1 | FormBodyWrapperFilter | Encapsulates request body |
| route | 1 | DebugFilter | Sets debug flags |
| route | 5 | PreDecorationFilter | Prepares request context |
| route | 10 | RibbonRoutingFilter | Routes by serviceId |
| route | 100 | SimpleHostRoutingFilter | Routes by URL |
| route | 500 | SendForwardFilter | Handles forward requests |
| post | 0 | SendErrorFilter | Processes error responses |
| post | 1000 | SendResponseFilter | Handles successful responses |
Custom Filter Implementation
Create custom filters by extending ZuulFilter and implementing four key methods. Consider a security scenario where requests must contain a valid token parameter:
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import com.netflix.zuul.context.RequestContext;
@Component
public class SecurityValidationFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return 5;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String accessToken = request.getParameter("accessToken");
if (StringUtils.hasText(accessToken)) {
context.setSendZuulResponse(true);
context.setResponseStatusCode(HttpStatus.OK.value());
context.set("validationSuccess", true);
} else {
context.setSendZuulResponse(false);
context.setResponseStatusCode(HttpStatus.BAD_REQUEST.value());
context.setResponseBody("Access token required");
context.set("validationSuccess", false);
}
return null;
}
}
Route Circuit Breaking
Implement graceful degradation when backend services fail by creating fallback providers. The modern approach extends FallbackProvider:
@Component
public class ServiceFallbackHandler implements FallbackProvider {
@Override
public String getRoute() {
return "product-service";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() {
return HttpStatus.SERVICE_UNAVAILABLE;
}
@Override
public InputStream getBody() {
return new ByteArrayInputStream(
"Service temporarily unavailable".getBytes()
);
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
Request Retry Mechanism
Enable automatic retries for transient failures using Spring Retry integartion:
// Add to application.properties
zuul.retryable=true
ribbon.MaxAutoRetries=2
ribbon.MaxAutoRetriesNextServer=0
Dependency configuration:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
Enable retry in main application class:
@SpringBootApplication
@EnableRetry
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
Testing reveals three retry attempts when services become unresponsive, demonstrating the resilience pattern in action.