Intelligent Routing#

Challenges with Individual Services#

  • Different ports

  • Different addresses

  • Different APIs and paths

Solution: “Intelligent Routing”

Routing Service#

  • Via Gateway service (or API Gateway)

  • Single point of entry for all clients

Gateway Service#

Provides:

  • Dynamic routing and delivery

  • Security and Filtering

  • Auditing and Logging

  • Request Enhancement

  • Load balancing

  • Different APIs for different clients

Netflix Zuul#

Zuul is a gateway service that provides dynamic routing, monitoring, resiliency, security and more.

  • Proxy server

  • Setting up routes

  • Setting up filters

Using Spring Cloud + Netflix Zuul Reverse Proxy#

Application.java

@SpringBootApplication
@EnableZuulProxy
public class Application {

  public static void main(String[] args) {
    SpringBootApplication.run(Application.class, args);
  }
}

Using Spring Cloud + Netflix Zuul#

With Service Discovery#

application.properties

spring.application.name=gateway-service
eureka.client.service-url.defaultZone=http://localhost:8761/eureka

OR application.yml

spring:
  application:
    name: gateway-service
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

Without Service Discovery#

application.properties

spring.application.name=gateway-service
ribbon.eureka.enabled=false

OR application.yml

spring:
  application:
    name: gateway-service
ribbon:
  eureka:
    enabled: false

Default Route to Service Resolution with Service Discovery

Request

maps to Service

/foo

spring.application.name=foo

/categories/1

spring.application.name=categories

NOTEs:

  • Prefix is stripped by default. Use zuul.stripPrefixes=false to disable.

  • All services are added by default. Use zuul.ignoredServices=<pattern> to ignore services.

With Service Discovery: Precise Routing#

application.properties

spring.application.name=gateway-service
zuul.routes.<route_name>.path=/somepath/**
zuul.routes.<route_name>.serviceld=some_service_id
zuul.ignored-services=some_service_id

OR application.yml

spring:
  application:
    name: gateway-service
  zuul:
    routes:
      ‹route_name>:
        path: / somepath/**
        serviceld: some_service_id
    ignored-services: some_service_id

Without Service Discovery: Precise Routing#

application.properties

spring.application.name=gateway-service
zuul.routes.<route_name>.path=/somepath/**
zuul.routes.<route_name>.url=http://some_url_address/

OR application.yml

spring:
  application:
    name: gateway-service
  zuul:
    routes:
      ‹route_name>:
        path: / somepath/**
        url: http://some_path_url/

Zuul Filters#

Filter types:

  1. Pre -> before request

  2. Route -> any kind of routing

    1. In fact @EnableZuulProxy sets up some default ‘route’ filters.

  3. Post -> After request is executed.

  4. Error -> Any of pre/route/post filter failed.

Creating a Zuul Filter#

Extend & Implement ZuulFilter

MyFilter.java

public class MyFilter extends ZuulFilter {
    // implement methods

    // Filter logic goes here
    // **Current implementation ignores return value**
    @Override
    public Object run() {
        // ...
    }

    // Whether or not the `run()` method should execute
    @Override
    public boolean shouldFilter() {
        // ...
    }

    // The type of filter: `pre`, `route`, `post`, `error`
    @Override
    public String filterType() {
        // ...
    }

    // The order of execution with respect to the other filters of the same type
    @Override
    public int filterOrder() {
        // ...
    }
}

Define a @Bean which returns the Filter

MyConfig.java

@Configuration
public class MyConfig {

    @Bean
    public ZuulFilter myFilter() {
        return new MyFilter();
    }
}

Sharing between Filters: RequestContext#

  • Holds request, response, state and data information

  • Only available for the duration of the request

RequestContext ctx = RequestContext.getCurrentContext();

// Get the servlet request
HttpServletRequest req = ctx.getRequest();

// Get the servlet response
HttpServletResponse = res = ctx.getResponse();

// Set a variable
ctx.set("foobar", "PRE_FILTER_EXECUTED");

// Get a variable
String foobar = (String) ctx.get("foobar");

Example of setting the header using Filter:

public class AbstractRequestHeaderFilter extends ZuulFilter {
    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.addZuulRequestHeader("x-location", "INDIA");
        return null;
    }
}