Introduction
When working with micro services it is common to have unified access-point to your system (also called API Gateway). Consumers only talk with the API Gateway and not with the services directly. This hides the fact that your system is composed out of multiple smaller services. The API Gateway also helps solving common challenges like authentication, managing cross-origin resource sharing (CORS) or request throttling.
Zuul is a JVM-based API Gateway developed and open-sourced by Netflix. In this post we will create a small Spring application that includes a zuul proxy for routing requests to other services.
Enabling zuul proxy
To use zuul in a project we have to add the spring-cloud-starter-netflix-zuul dependency. If we want to use the spring zuul actuator endpoint (more on this later), we also need to add the spring-boot-starter-actuator dependency.
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <!-- optional --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
Next we have to enable the zuul proxy using @EnableZuulProxy in our spring boot application class (or any other spring @Configuration class)
@SpringBootApplication @EnableZuulProxy public class ZuulDemoApplication { ... }
Now we can start configuring our routes.
Configuring routes
Routes describe how incoming requests should be routed by zuul. To configure zuul routes we only have to add a few lines to our spring boot application.yml (or application.properties) file:
application.yml:
zuul: routes: users: path: /users/** url: https://users.myapi.com projects: path: /projects/** url: https://projects.myapi.com
Here we define the routes for two endpoints: /users and /projects: Requests to /users will be routed to https://users.myapi.com while requests to /projects are routed to https://projects.myapi.com.
Assume we start this example application locally and send a GET request to http://localhost:8080/users/john. This request matches the zuul route /users/** so zuul will forward the request to https://users.myapi.com/john.
When using a service registry (like Eureka) we can alternatively configure a service id instead of an url:
zuul: routes: users: path: /users/** serviceId: user_service
Another useful option is sensitiveHeaders, which allows us to remove headers before the request is routed to another service. This can be used to avoid leaking of sensitive headers into external servers (e.g. security tokens or session ids).
zuul: routes: users: path: /users/** url: https://users.myapi.com sensitiveHeaders: Cookie,Set-Cookie,Authorization
Note that the shown example headers (Cookie,Set-Cookie,Authorization) are the default value of the sensitiveHeaders property. So these headers will not be passed, even if sensitiveHeaders is not specified.
Request / Response modification with filters
We can customize zuul routing using filters. To create a zuul filter we create a new spring bean (marked with @Component) which extends from ZuulFilter:
@Component public class MyFilter extends ZuulFilter { @Override public String filterType() { return FilterConstants.PRE_TYPE; } @Override public int filterOrder() { return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext context = RequestContext.getCurrentContext(); context.addZuulRequestHeader("my-auth-token", "s3cret"); return null; } }
ZuulFilter requires the definition of four methods:
- Within filterType() we define that our filter should run before (PRE_TYPE) the actual routing. If we want to modify the response of the service before it is send back to the client, we can return POST_TYPE here.
- With filterOrder() we can influence to order of filter execution
- shouldFilter() indicates if this filter should be executed (= calling the run() method)
- in run() we define the actual filter logic. Here we add a simple header named my-auth-token to the request that is routed to another service.
Filters allow us to modify the request before it is send to the specified service or to modify the response of the service before it is send back to the client.
Actuator endpoint
Spring cloud zuul exposed an additional Spring Boot actuator endpoint. To use this feature we need to have spring-boot-starter-actuator in the classpath.
By default the actuator endpoint is disabled. Within application.yml we enable specific actuator endpoints using the management.endpoints.web.exposure.include property:
management: endpoints: web: exposure: include: '*'
Here we simply enable all actuator endpoints. More detailed configuration options can be found in the Spring Boot actuator documentation.
After enabling the zuul actuator endpoint we can send a GET request to http://localhost:8080/actuator/routes to get a list of all configured routes.
An example response might look like this:
{ "/users/**":"https://users.myapi.com", "/projects/**":"project_service" }
Summary
With Spring cloud you can easliy integrate a zuul proxy in your application. This allows you the configuration of routes in .yml or .properties files. Routing behaviour can be customized with Filters.
More details on spring's support for zuul can be found in the official spring cloud zuul documentation. As always you can find the examples shown in this post on GitHub.
Leave a reply