golang
May 1, 2023

đŸ‘č How to create a λ-function for the Golang Microservice?

In the current article, we propose to consider the possibility of implementing the microservice's module by using λ-function.


Any actively developing microservice strives to implement and successfully maintain certain architectural features, such as technological heterogeneity, stability, scalability, ease of deployment, and composability.

One of the approaches to the development of the microservice philosophy is to respect the decomposition of its architecture by using shared libraries, as well as allocation of individual modules. Thus, a microservice presented as a set of parts can create both a homogeneous and a heterogeneous environment, when the components are implemented in different programming languages, or with a help of independent third-party resources.

Decomposition allows not only to divide, but also to reduce the size of microservice's modules (MM). Yet, it's fairly difficult to predict specific time when MM becomes independent enough to serve as a λ-function. In view of the above, MM may contain common global variables, a common logical part and libraries, but shouldn't be directly executed from the main service.

There are no precise requirements when choosing the right candidate to serve as a λ-function, except for the fact that a suitable candidate should be both consistent and compliant with the general microservice's philosophy and strive to be a part of the program. Thus, the requirements for the MM executed within the λ-function should be more stringent than for the microservice itself. In the following, the MM which is planned to be performed using the λ-function, will be called the λ-module. It’s necessary to highlight that the functionality of the λ-module is limited to one or a couple of functions.

A candidate for execution in a λ-function can be represented by a procedure executed in parallel, or by preconfiguration of microservice, such as a database migration process.

In the current article, we propose to consider the possibility of implementing the MM by using λ-function.

Problem Definition

Although the current version of microservice's template (MT) meets all the requirements, such as stability, scalability, ease of deployment and composability; some parts are still to be modified. For instance, the MM is to be further refined. In view of the foregoing, the implementation MM as part service has a number of issues discussed below:

  1. The MM launches as soon as the service is built. This approach mixes service and MM work, while they are logically independent from one another. With that in mind, this service shouldn’t be directly involved in execution MM scripts, yet the MM shouldn’t directly affect any part of the service.
  2. In percentage terms, the MM can occupy a large part of the microservice. The code of the MM and its testing are distributed over different parts of the microservice, which may complicate the process of supporting the module.
  3. Testing MM should be independent from testing the service. There are many cases where developers mix interface and unit testing, and the reason for this lies in the inseparable connection between the service and MM.

An advantages of using λ-function

The MT development seeks to minimize and standardize all the components. One way to reduce the size of the MT and increase the composability of the MT is to implement this component as a standalone λ-module used by λ-function.

  • Composability. The service launch MM processes are separated within MT. The λ-module contains all necessary parts in one place and is being executed by independent λ-function.
  • Homogeneity. Due to the need to maintain the service homogeneity, the λ-function, as well as testing all parts of the MT, must be performed in the same programming language.
  • Continuity & Visibility. The Canaries Testing can be used for running Golang interface-tests packed in the standalone λ-function. It is possible to set up a periodic launch of λ-function using AWS::CloudWatch::Synthetics protocol, with logging information on the execution, processing and storing screenshots in AWS::S3 storage.
  • Stability. Working with the database can be accessed from the λ-function and, as a result, it becomes possible to simplify interface-testing associated with checks for the existence of schemas, tables, and other data. Thus, service interface-testing focuses on testing service endpoints, while λ-module testing becomes an independent process.
  • Ease of deployment & Scalability. Using the λ-function ensures ease of startup, decouples the service from the λ-module, and makes the code more efficient and smaller, enabling the ease of maintenance. The number of λ-functions used by the MT is unlimited and can be replenished anytime with new independent λ-modules. The AWS Serverless Application Model (SAM) can be used to run and test the λ-function locally.

Implementation

The program name of λ-function will be <λ-function>. All new λ-function will be placed in the lambdas directory.

The SAM is used to evaluate the performance testing of the λ-function. To configure SAM, you need to prepare the template.yaml file, which contains the configuration of the λ-function being developed.

The code that implemented using the λ-function is located in the <λ-function>.go file.

The general view of the λ-function architecture is presented below:

/repo root
|--...
|--/lambdas
|----/<λ-function>
|------template.yaml
|------<λ-function>.go

Configuration of the λ-function

The template.yaml file contains a λ-function's configuration used to specify the name of the λ-function, the package with the λ-function source code and accompanying files, the name of the λ-function's handler, and the version of the driver that will be used to execute the λ-function code.

Resources:
	<λ-function>Function:
	Type: AWS::Serverless::Function
	Properties:
		CodeUri: <λ-function>.zip
		Handler: <λ-function>
		Runtime: go1.x
		Tracing: Active

In the Event block, we must specify the type, link, and method that the endpoint will use. It's a very useful if we need to separate interface, unit and integration tests for a service. We need always remember that testing the database queries not the same as interface testing.

Properties:
    ...
    Events:
	    <name of event>:
	        Type: Api
	        Properties:
	        Path:   <url>
	        Method: <http method type>
    Environment:
	    Variables:
	        ...

Note, that the resource's property CodeUri can store a path of folder with executable file or path to *.zip archive, which contain a not only executable file, but additional files with, for example, *.sql scripts. We call this archive the λ-package, which can be created with next simple commands in the root of the service:

go build <repo root>/lambdas/<λ-function>/<λ-function>.go && / mv <repo root>/<λ-function> <repo root>/lambdas/<λ-function>/

zip <repo root>/lambdas/<λ-function>/<λ-function>.zip -j / <repo root>/lambdas/<λ-function>/<λ-function>

Keep in mind that the size of the λ-package should not exceed 10 MB and, if this condition is violated, Amazon S3 must be used.

The executable <λ-function> file and all additional components are run every time the λ-function is called. There is no need to vendor the third-party code as the λ-function allows you to use the compiled code with everything you need.

How to launch the λ-function?

For example, the <λ-function>.go file need to store a main function with lambda handler.

import (
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	return events.APIGatewayProxyResponse{
		Body:       "> Hello from λ!",
		StatusCode: 200,
	}, nil
}

func main() {
	lambda.Start(handler)
}

The lambda.Start(handler) command launches the handler function, which returns a message in JSON format with StatusCode and Body. For local execution of the λ-function using SAM, the following commands must be executed:

sam build --template-file <repo root>/lambdas/<λ-function>/template.yaml

sam local start-api

If everything is prepared successfully, we can see the message:

Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

In order to make sure that everything is working correctly, you need to send an HTTP request using curl and get a response from the λ-function:

curl http://127.0.0.1:3000/

> Hello from λ!

Repository

See the sample of a Golang λ-function in my repo:

🍃 GO TO REPO 🍃

Contents

  1. AWS Documentation: AWS Lambda.
  2. AWS Documentation: What is SAM?