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:
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:
Building Zuul Edge Service
Start with a basic SpringBoot application. Add Zuul compile dependency:
compile("org.springframework.cloud:spring-cloud-starter-zuul:$springCloudVersion")
Annotate application class with @EnableZuulProxy
attribute:
@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
}
}