Friday, January 02, 2015

Microservices: Netflix OSS Reference Architecture Study

Netflix provides sample distributed microservices application (RSS server) that is comprised of:
  • edge server (responds to requests from RSS clients and gets feeds from middle-tier server)
  • middle-tier server (pulls RSS feeds from content providers)
  • services registry (eureka)

How to Build

RSS middle-tier and edge servers can be built and run using instructions provided here:
Eureka can be built using instructions here:

How to Run

Follow instructions here:
Note that:
  • Default configuration requires that eureka be running on port 80, which would require you to launch the tomcat using sudo (as well as modify server.xml).
  • Eureka services registry may be persisted using cassandra, or in-memory without using cassandra.  The default is to use the in-memory persistence.
  • Eureka by default is configured to talk to AWS, and there will be exceptions during start up if AWS properties aren't configured – however these exceptions may be safely ignored.
At the end the application is running at 

Application Architecture

Deployment

There are 3 processes
  • Eureka is running in tomcat container.
  • RSS middle-tier is running in jetty container
    • port 9191 is netty server port
      # Netty Configuration
      netty.http.port=9191
      netty.http.host=0.0.0.0
       
    • port 9192 is healthcheck server port
      # Health Check Handler
      com.netflix.karyon.health.check.handler.classname=com.netflix.recipes.rss.manager.MiddleTierHealthCheckHandler
      netflix.platform.admin.resources.port=9192
      com.netflix.karyon.unify.health.check.with.eureka=true
       
  • RSS edge server is running in jetty container 
    • port 9090 is jetty port (this is the server user interacts with via browser http://localhost:9090/jsp/rss.jsp)
      # Jetty Configuration
      jetty.http.port=9090
       
    • port 9092 is healthcheck server port
      # Health Check Handler
      com.netflix.karyon.health.check.handler.classname=com.netflix.recipes.rss.server.EdgeHealthCheckHandler
      netflix.platform.admin.resources.port=9092
      com.netflix.karyon.unify.health.check.with.eureka=true

Middle-tier Server

Class MiddleTierResource.java implements REST Service using jax-ws annotations.  Servo metrics are maintained by the same class and registered with servo which exposes them.
Example GET method in AddRSSCommand.java:
@GET
@Path("/rss/user/{user}")
@Produces({MediaType.APPLICATION_JSON})
public Response fetchSubscriptions (final @PathParam("user") String user) {
// Start timer
Stopwatch stopwatch = getRSSStatsTimer.start();
try {
getRSSRequestCounter.increment();
Subscriptions subscriptions = RSSManager.getInstance().getSubscriptions(user);
return Response.ok(subscriptions).build();
} catch (Exception e) {
logger.error("Exception occurred when fetching subscriptions", e);
getRSSErrorCounter.increment();
return Response.serverError().build();
} finally {
stopwatch.stop();
getRSSStatsTimer.record(stopwatch.getDuration(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
}
}
Communication between edge server and middle tier server.  Example: edge server requests subscriptions:
GET /middletier/rss/user/default HTTP/1.1
Netflix.NFHttpClient.Version: 1.0
X-netflix-httpclientname: middletier-client
Host: C02LG18GF1G3.local:9191
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.2 (java 1.5)
HTTP/1.0 200 OK
Content-Type: application/json
{"subscriptions":[{"items":[{"description":"
 Communication between middle-tier server and RSS content servers.  Middle-tier server acts as a client and uses ribbon to pull RSS feeds.  Example in RSSManager.java:
 

/**
* Fetch the RSS feed content using Ribbon
*/
private RSS fetchRSSFeed(String url) {
 
RestClient client = (RestClient) ClientFactory.getNamedClient(RSSConstants.MIDDLETIER_REST_CLIENT);
HttpClientResponse response;
String rssData = null;
 
try {
HttpClientRequest request = HttpClientRequest.newBuilder().setUri(new URI(url)).build();
response = client.execute(request);
 
if (response != null) {
rssData = IOUtils.toString(response.getRawEntity(), Charsets.UTF_8);
logger.info("Status code for " + response.getRequestedURI() + " : " + response.getStatus());
}
} catch (URISyntaxException e) {
logger.error("Exception occurred when setting the URI", e);
} catch (Exception e) {
logger.error("Exception occurred when executing the HTTP request", e);
}
 
return parseRSS(url, rssData);
}

Technologies used by application: 
  • jetty (http/servlet container, e.g. like tomcat)
  • jersey (REST services framework)
  • netty (NIO client framework) 

Edge Server

Acts as a client talking to the middle-tier server, see sample JSON request in the section above.
Each request to the middle-tier server is wrapped in a hystrix command.  Example:

/**
* Calls the middle tier Add RSS entry point
*/
public class AddRSSCommand extends HystrixCommand {
// RSS Feed Url (encoded)
private final String url;
public AddRSSCommand(String url) {
super (
Setter.withGroupKey(
HystrixCommandGroupKey.Factory.asKey(RSSConstants.HYSTRIX_RSS_MUTATIONS_GROUP))
.andCommandKey(HystrixCommandKey.Factory.asKey(RSSConstants.HYSTRIX_RSS_ADD_COMMAND_KEY))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(RSSConstants.HYSTRIX_RSS_THREAD_POOL)
)
);
this.url = url;
}
@Override
protected String run() {
try {
/*
* The named client param must match the prefix for the ribbon
* configuration specified in the edge.properties file
*/
RestClient client = (RestClient) ClientFactory.getNamedClient(RSSConstants.MIDDLETIER_REST_CLIENT);
HttpClientRequest request = HttpClientRequest
.newBuilder()
.setVerb(Verb.POST)
.setUri(new URI("/"
+ RSSConstants.MIDDLETIER_WEB_RESOURCE_ROOT_PATH
+ RSSConstants.RSS_ENTRY_POINT
+ "?url=" + url))
.build();
HttpClientResponse response = client.executeWithLoadBalancer(request);
return IOUtils.toString(response.getRawEntity(), Charsets.UTF_8);
} catch (Exception exc) {
throw new RuntimeException("Exception occurred when adding a RSS feed", exc);
}
}
@Override
protected String getFallback() {
// Empty json
return "{}";
}
}

JMX

Both the edge server and the middletier server expose JMX metrics (mbeans in package com.netflix.servo).

No comments: