Evolving API platform using Spring based Serverless microservices

2016-12-27
api/ architectures

You are on AWS and you are in the high growth phase. You are going through a lot of hiring
, componentizing your existing applications, scaling your infrastructure and so on to
meet projected future demands. That’s a common story across enterprises old or new.

Along with tackling all these challenges, you want to make smarter decisions and want
your Tech Team to focus on business deliverables as much as you can. Great, you are
thinking in right direction as that’s the only way you can have a competitive edge
over the competitors in your space!

Your Tech team is attending Tech events and hearing a lot about Serverless and all
its benefits. Having spent most of your time in Tech, you know that it’s been a long
journey from the data center to IAAS to PAAS to Serverless but worth it.

You decide to use serverless for your evolving platform needs. Being in AWS, you can use
AWS Lambda service to create function as a service. You quickly create a prototype
Lambda with the language of your choice, let’s say Java for example.

Everything works and more and more teams start adopting serverless computing and
function as the unit of scale.

As you gain more confidence in serverless computing by using AWS Lambda, you also
decide to migrate your monolith to serverless platform. You quickly create a lambda
function, add logic to parse input Lambda events and redirect to the different method or
event handlers, deserialize the response. Other Teams also start using your solution and
start realizing benefits of serverless computing.

Now, you have 10+ micro services, each with their own ways of handling non-functional
requirements such as exception handling, authentication, authorization etc. Assuming you
are following the code as a configuration and immutable infrastructure paradigm, you
realize that for each API endpoint, you need to add method definition in your cloud
formation. This looks okay initially but your cloud formation starts bigger and bigger.

At this time, your architecture looks as below diagram.

lambda-serverless-container

As your micro service ecosystem grows, you start realizing that each team needs to
write more and more code for handling non-functional requirements such as exception
handling, distributed tracing, logging, authentication, authorization etc. You wear
your architect hat and start thinking about how this could be managed better.

Most of these non-functional requirements are handled in web applications by using web
frameworks such as Jersey, spring MVC transparently.

Wouldn’t it be nice if you could run your fully fledged web applications inside AWS Lambda?

Is it even possible?

You start exploring the options before building it yourself! You find that it’s
possible. AWS has open sourced AWS serverless java container library which helps to run
Spring, Jersey, Spark or other apps in lambda.

lambda-serverless-container

If you are curious and want to see how it could be achieved, it’s as simple as below example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class StreamLambdaHandler implements RequestStreamHandler {
private static SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
static {
try {
handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class);
} catch (ContainerInitializationException e) {
// if we fail here. We re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring framework", e);
}
}

@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
handler.proxyStream(inputStream, outputStream, context);

// just in case it wasn't closed by the mapper
outputStream.close();
}
}

You quickly prototype a reference micro service and test how it would work and see it
could be a value add to your project. You create reference micro service which runs
Spring MVC apps inside Lambda and started using it in our newer micro services
initiatives to try this newer paradigm and see how it works in your ecosystem. As more
and more teams start using this paradigm, you understand the benefits and painpoints
of serverless paradigm.

Above is the similar (if not exact) journey we went through at Financial Engines.

If you are curious about what does this reference micro service contain then read on. Here
are high-level components we created to make it work

  • API gateway API Cloud formation creates AWS API gateway API to create a proxy to
    our lambda function.
  • Lambda function contains a sample spring application. Tech stack is Spring MVC,
    Java 8, Gradle, Jenkins, Jenkins Job Builder. We already are using various Gradle
    plugins for artifact management using Artifactory, code quality management using
    SONAR, find bugs, PMD etc.
  • Greedy Proxies - We are using greedy proxies in api gateway which helps us to do
    proxy method calls to lambda and developers no longer need to add the mapping for
    each method in cloud formation and glue code in lambda. Definitely some saving in developer time!
  • Uses Financial Engines best practices already such as Jenkins Job builder for CI / CD,
  • Hexagonal pattern for driving domain driven design, FE authorization library to
    annotate methods based on role/scope, standard logging interceptor to enable
    distributed tracing, Cloudwatch logging via API gateway to enable tracing for API gateway calls etc
  • It will also help us to standardize on Standard error format using a common library.
  • It was a pain with current AWS implementation as each and every team was
    duplicating the code in their code bases as well as it required API gateway trickery
    to map error response to standard HTTP codes in method responses.

lambda-serverless-container

As we wanted to scale our development and wanted to avoid adding each endpoint in
cloudformation for each endpoint in the application

api_painpoints

AWS API gateway has also released greedy proxies feature which makes developer’s life
really easy. If you are new to API gateway, greedy proxies help us declare /* paths
in API gateway so that your developer does not need to add API
method in cloud formation for each API endpoint.

You can just declare the base path and all paths under it would be handled by the
integration endpoint such as LAMBDA_PROXY or HTTP_PROXY.

using_greedy_proxies

Overall, template helps us running micro service running within 15-30 minutes including
Api gateway APIs, lambda function, custom domain mapping, Jenkins pipeline etc.

We are working on the implementation where we will be able to create newer
micro services using UI like any other rapid application development framework with
Financial Engines specific conventions.

So what benefits are we expecting out of it? As most of our developers already know
spring, the learning curve is very less. Spring code becomes reusable and you can run
the same code outside lambda as well. This can help if you want to run your application
locally in web server. Development is very intuitive as no changes are required in
cloud formation to add new methods. We are using the greedy proxy mechanism provided
by API gateway to implement this. All Web framework benefits - Ability to add
interceptors, validation, cookies etc. Without this framework, each team had to do
all of them on their own and required duplication in some ways.

We developed a separate Lambda for handling our auth needs similar to below architecture.

lambda-serverless-container

We are also aware that this could have possibly some downsides. Most common of them
are Lambda Boot up time for such applications is a little bit higher than traditional
lambda application. This may have downsides if your application grows bigger in size
however that may point to other bigger problems as to if it’s time to evaluate if
your micro service is big enough.

In many other cases, It also depends on your organization’s set SLA’s and thresholds.

You can mitigate some of these concerns by pre-warming lambdas or in other use cases
might realize that Elastic BeanStalk or ECS may be a better choice.

As always, evaluate you use case before riding the serverless bus!


Comments: