Edge Service pattern

This Kata is based on the assumption that you’re running the Config service, Eureka service (Kata6), Profiles Service (Kata6) and Acounts service (Kata6) in Docker.
$ docker run -d --name="config-service" -p 8888:8888 msvcdojo/config-service:0.0.1
$ docker run -d --name="eureka-service" -e config-service.uri="http://192.168.99.100:8888" -p 8761:8761 msvcdojo/eureka-service:0.0.1
$ docker run -d --name="demo-mongo" -p 27017:27017 mongo:2.2.7
$ docker run -d --name="profiles-service" -e mongoserver="192.168.99.100" -e config-service.uri="http://192.168.99.100:8888" -e service-registry.uri="http://192.168.99.100:8761" -e external.ip="192.168.99.100" -p 8101:8101 msvcdojo/profiles-service:0.0.2
$ docker run -d --name="demo-mysql" -e MYSQL_ROOT_PASSWORD="password" -e MYSQL_DATABASE="demo" -e MYSQL_USER="demo_user" -e MYSQL_PASSWORD="demo_pass" -p 3306:3306 mysql:5.6
$ docker run -d --name="accounts-service" -e sqlserver="192.168.99.100" -e config-service.uri="http://192.168.99.100:8888" -e service-registry.uri="http://192.168.99.100:8761" -e external.ip="192.168.99.100" -p 8122:8122 msvcdojo/accounts-service:0.0.2

By this time the architecture of our simple application looks like this:

start architecture

So far we were not exposing any services to the external clients. External clients may include but not limited to:

  • Web applications

  • External customers and businesses

  • etc…​

External connections should not be allowed to have direct access to our internal services without any control, load balancing, sanitization, routing, etc…​

We don’t want to expose our internal architecture to the rest of the world so we need to bring in an Edge service (API Gateway service).

Netflix has solved this problem and open sourced Zuul - their

After adding a Zuul API Gateway our architecture will look like this:

end architecture

Building Zuul Edge Service

Start with a basic SpringBoot application. Add Zuul compile dependency:

gateway-service/build.gradle
compile("org.springframework.cloud:spring-cloud-starter-zuul:$springCloudVersion")

Annotate application class with @EnableZuulProxy attribute:

src/main/java/msvcdojo/GatewayServiceApplication.java
@EnableZuulProxy
public class GatewayServiceApplication {

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

It’s time to create routes. See in the following block how serviceId is mapped to an externally-accessible proxy path:

zuul:
  ignoredServices: '*'
  routes:
    accounts-service:
      path: /accounts/**
      serviceId: accounts-service
      stripPrefix: true
    profiles-service:
      path: /profiles/**
      serviceId: profiles-service
      stripPrefix: true
Make sure to expose ONLY services that are intended for consumption by external clients. Note the ignoredServices: '*' setting - it blocks access to the services that have no mapping in the configuration.

Build and launch gateway service:

$ gradlew clean prepDocker
...
$ java -jar -Dconfig-service.uri=http://192.168.99.100:8888 -Dservice-registry.uri=http://192.168.99.100:8761 build\libs\gateway-service-0.0.1.jar

Play time

Now let’s see how Zuul routes our requests.

$ curl http://localhost:8111/profiles/profiles
{
  "_embedded" : {
    "profiles" : [ {
      "fullName" : null,
      "key" : "john",
      "photoCount" : 0,
      "_links" : {
        "self" : {
          "href" : "http://localhost:8111/profiles/profiles/john"
        },
        "profile" : {
          "href" : "http://localhost:8111/profiles/profiles/john"
        },
        "photos" : {
          "href" : "http://localhost:8111/profiles/profiles/john/photos"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8111/profiles/profiles"
    },
    "profile" : {
      "href" : "http://localhost:8111/profiles/profile/profiles"
    },
    "search" : {
      "href" : "http://localhost:8111/profiles/profiles/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
Take a note on how all the URIs look like. They’ve all been modified to hide the internal information and now all of them point to Zuul.

Now what about our Accounts service?

$ curl http://localhost:8111/accounts/accounts
{
  "_embedded" : {
    "accounts" : [ {
      "username" : "john",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8111/accounts/accounts/1"
        },
        "account" : {
          "href" : "http://localhost:8111/accounts/accounts/1"
        },
        "profile" : {
          "href" : "http://localhost:8111/profiles/profiles/1/photos"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8111/accounts/accounts"
    },
    "profile" : {
      "href" : "http://localhost:8111/accounts/profile/accounts"
    },
    "search" : {
      "href" : "http://localhost:8111/accounts/accounts/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}