Micro-services Using go-kit: Service Discovery

In a environment where number of instances and network locations changes dynamically, a client needs a discovery mechanism to determine the location of a service instance, in order to send a request to it.

Sharing is caring!

Overview

service discoveryNowadays, a micro-service based application usually runs in a containerized environments. The main reason for this approach because it is essential for a service to be scaled individually based on its resources need.  Container itself has a capability to bring flexibility in terms of adding number of instances and changing network locations. However, this dynamic-ism brings another effect to our client code. We have to discover a service to which request will be sent. This consequences lead us to a service discovery mechanism.

Client Side Service Discovery
http://microservices.io/patterns/client-side-discovery.html

Basically, a service discovery is a mechanism to a client to find a service without the needs to know which is a service is located. Rather, the client will query through a service registry to obtain the location of service instances. Then the client will send a request to its associate instance.

Server Side Service Discovery

http://microservices.io/patterns/server-side-discovery.html

In this mechanism, rather than a client query through a service registry, it sends the requests through a router. The router then will query the service registry and forward the requests to a service instance.

 

Consul

Briefly, consul is a tool for discovering and configuring services in your infrastructure. It provides several key features such as service discovery, health checking, key-value Store and multi data center.

For the purpose of this article, I will use two consul key features. The first one, of course, the service discovery feature. The other one is health checking feature. This feature basically check whether a service is still in a healthy condition in order to process requests.

For more details about consul and its features, you can visit consul website.

FYI, consul is not the only available tools for service registry. There are several tools in the market such as etcd and Apache Zookeeper.

Use Case

This article will use client side service discovery mechanism. First, I will register Lorem Service into consul. Then I will create a client to invoke Lorem Service after querying the registry. The client itself will expose a service under sd-lorem context path. Moreover, I will create health check function under existing service in order to provide consul the information whether the service is healthy or not.

Usually, Lorem Service only invoke URL without payload and then return the result. Instead, sd-lorem endpoint will be a little bit different. I will invoke endpoint with POST method and pass the payload. The payload will be in the JSON format, with structure:

In addition, I will run the consul via docker and use progrium/consul image. Since this is article purposely to show how service discovery works with consul, I only run a single consul server. However, this is not recommended for production because you need the cluster setup. For more information, read the guideline on their website.

Step by Step

First of all, copied the lorem-metrics folder and give it a name lorem-consul to a new folder. Then start a consul agent by issuing this command on shell:

Step 1: service.go

Add a new function and give it a name HealthCheck then implement the new function. The function takes no input and return a boolean value.

Note: the HealthCheck implementation is very simple. It only returns true so the response code will be 200 OK. However, this is a bad idea to always return true in production environment.

Step 2: logging.go and instrument.go

In the previous article, we extend the Service interface to loggingMiddleware struct and metricsMiddleware struct. Then we implemented the service function for each struct. This would bring another consequences, which we have to implement the new HealthCheck function to each struct.

logging.go

instrument.go

Step 3: endpoint.go

This step is pretty clear because we need to expose a health endpoint. Then consul can consume this endpoint to determine the health status.

Step 4: transport.go

After creating the endpoint function, then we need to add a new HTTP handler for HealthCheck function. Then the function will be exposed under /health context path. As usual, at first we create a decode function for Health request.

Step 5: registration.go

This is a new file for handling service registration to consul. In this file, we will create a new function with name Register. This function takes four input parameters, which are:

  • Consul Address. The IP address of consul agent.
  • Consul Port. The port of consul agent.
  • Advertised Address. Service instance IP address.
  • Advertised Port. Service instance port.

The function then will return Registrar interface from go-kit library.

Basically, the Register function will use consul API to register the service as well as its health monitoring endpoint.

Note: see highlighted lines to identify the specific service. Consul uses service name and tags to query the registry.

Step 6: lorem-consul.d/main.go

In this file, we add a function to create a health endpoint then register service to consul by calling Register function. Next we call the registar.Register() function before starting the HTTP server. Then implement registar.Deregister() function whenever the HTTP server is terminated.

Note: see highlighted line for register to and deregister from consul.

Step 7: discover.d/main.go

In this step, we create a folder discover.d and create a main.go file. Because our client uses different payload format compare to existing Lorem Service, first of all we need to decode and encode the request from our client.

Next we create a function to encode and decode the response. Because the client will return the same payload with existing Lorem Service, we do not need to implement new encode functionality. Rather, we will use existing EncodeResponse function. So we just need to implement the decode function.

Next, create a factory function to construct the URL from service discovery, set encode and decode function then parse it into an endpoint.Endpoint function.

Go into main function, in this function we construct a consul client, subscribe to consul agent then create an HTTP handler from the factory endpoint.

Note: see highlighted lines. We use the same tags and service name from Register function (Step 5).

Step 8: Run the Sample

For this sample, I will run two instances and register them to consul. They are running on port 7002 and 7003 respectively.

Note: remember consul agent already running via docker.

After services started, you will see in the console similar output:

And in the consul UI:

consul ui consul ui

And now run the client:

Then try to make a request via curl:

Advance Topic

In the previous article, we talk about API monitoring using Prometheus and Grafana. We still want to monitor our API, but now it is a little bit complex, because we cannot put in a static config way our scrape target. Just remember about dynamic-ism of micro services architecture.

Luckily, Prometheus provides consul configuration natively. Scraping of all instances can be easily achieved by consul_sd_config and relabeling config. The setup is configured through prometheus.yml

prometheus.yml

Note: see __meta_consul_tags for identifying service name lorem.

Then run Prometheus and Grafana server to scrape our instances. After making several requests, you will see the metrics from two instances.

prometheus
Prometheus
Grafana

 

 

 

 

Note: see lorem-consul/README.md for running these applications. And I am sorry for the screenshots. They are using port 7004 instead of 7003. This is because I run multiple times before capturing the results. And I mixed it with multiple ports. But, need no worry, the main point is showing metrics from multiple instances.

Summary

One of the advantages of micro-services architecture is each service can be scaled individually based on its resource needs. But this bring another consequence, which is the needs of service registry.

Consul has provided a great tools to handle this mechanism, through its service discovery functionality. Moreover, go-kit itself, as a micro-service framework, already shipped with service discovery library under github.com/go-kit/kit/sd. Therefore, both of them help us a lot to overcome the needs of service registry. So, thanks to you who provide this nice cool tools and libraries.

That’s all for today. Happy Passover. Happy Easter.

PS: complete code is on my github, under folder lorem-consul.

References

Author: ru rocker

I have been a professional software developer since 2004. Java, Python, NodeJS, and Go-lang are my favorite programming languages. I also have an interest in DevOps. I hold professional certifications: SCJP, SCWCD, PSM 1, AWS Solution Architect Associate, and AWS Solution Architect Professional.

Leave a Reply

Your email address will not be published. Required fields are marked *