Having a well structured and reusable api-led architecture is a key factor to deliver a successful product to your customer, but also implementing a common monitoring system that sends alerts when some of your api fails can make the difference between a good and a great solution.

In this article we will show how to set up a monitor that periodically check if ours mule apps are healthy leveraging Anypoint Monitoring feature.

Introduction

In order to achieve our goal we should add a couple of new endpoint to each apps, this endpoints will provide information about the status of the apps and of its dependencies. This solution will add the following endpoints to each app of our system:

  • heartbeat – check the status of the api only.

  • healthcheck – check the status of the api and its dependencies.

RAML Configuration

We will use a RAML fragment to defines once the data type we will use across ours api:

#%RAML 1.0 Library
usage: Healthcheck utility datatypes
types:
  heartbeat:
    description: A data type to for the heartbeat
    properties:
      status:
        enum:  ["healthy","unhealthy"]
        required: true
      serviceName:
        type: string
        required: true
      type:
        enum:
          - xapi
          - papi
          - sapi
          - backend
        required: true
      message: 
        type: string
        required: false

  healthcheck:
    description: A data type to checkes health-check of its dependecy
    type: heartbeat
    properties:
      dependencies:
        type: array
        items: heartbeat
        required: false

Once the library is defined and published on Anypoint Exchange we can add the monitoring endpoint to ours api.

First import the exchange module in your main raml file:

uses:
monitoring-lib: exchange_modules/.../healthcheck-library.raml

Then add to your main RAML file the heartbeat and healthcheck endpoints based on your api type:

  • System API: heartbeat
  • Process API: heartbeat + healthcheck
  • Experience API: healthcheck
/healthcheck:
  displayName: Health Check
  get:
    displayName: Health Check
    description: Returns the status of the API and its dependencies
    responses:
      200:
        body:
          application/json:
            type: monitoring-lib.healthcheck
            example: !include exchange_modules/.../healthcheck-example.json
 
/heartbeat:
  displayName: Heartbeat
  get:
    displayName: heartbeat
    description: Returns the status of the API
    responses:
      200:
        body:
          application/json:
            type: monitoring-lib.heartbeat
            example: !include exchange_modules/.../heartbeat-example.json

Implementation

Now let’s see how we can implement a common flows that can be used across our apis to implements the monitoring endpoints defined above, in this example we will use Mule 3 but it can be easily adapted to Mule 4 also.

Heartbeat subflow

Inside your common module implement a simple SubFlow that will return infos about the status of the api

 

It’s a really simply flows that return info of the called api:

  • status: healthy (if is unhealthy it will no respond at all)

  • serviceName: taken from property app.name (this property is always available and you don’t have to worry about different name for the same api for different environment)

  • type: sapi/papi/xapi calculated from the app.name using a dataweave function (explained later)

Healthcheck subflow

 

Use a for-each scope to cycle the dependencies passed as input payload and for each one invoke the corresponding heartbeat/healthcheck api (this can be configured from the api referencing this flow, explained later)

Use a choice to be able to work with both HTTP and HTTPS request, the http request configuration use flow vars to dynamically call endpoint on different hosts, port and path

<http:request-config 
    name="HTTPS_Request_Configuration_hearthbeat" 
    host="#[flowVars.host]" port="#[flowVars.port]" 
    doc:name="HTTP_Request_Configuration_hearthbeat" 
    basePath="#[flowVars.basePath]" protocol="HTTPS">
	<tls:context>
		<tls:trust-store insecure="true"/>
	</tls:context>
</http:request-config>
	
<http:request-config 
    name="HTTP_Request_Configuration_hearthbeat" 
    host="#[flowVars.host]" port="#[flowVars.port]" 
    doc:name="HTTP_Request_Configuration_hearthbeat" 
    basePath="#[flowVars.basePath]" />

Create a Dataweave function used by both heartbeat and healtcheck subflow to determinate the api-led layer of the current api from its name.

Use a regex and assume that the api name follow one of this 2 standard:

  • somewhere in the api name there a char sequence of this types

    • -s- for system api

    • -p- for process api

    • -e- for experience api

  • somewhere in the api name there a char sequence of this types

    • -sapi- for system api

    • -papi- for process api

    • -xapi- for experience api

%dw 1.0
%function parseType(serviceName) 
"sapi" when serviceName matches /(.+-s-.+|.+-sapi-.+)/ otherwise
"papi" when serviceName matches /(.+-p-.+|.+-papi-.+)/ otherwise
"xapi" when serviceName matches /(.+-e-.+|.+-xapi-.+)/ otherwise "unknown type"
---
{
callFunctionParseType: parseType
}

Once your common module is complete build it as a .jar file and install it in your maven repository in order to be able to import it in the project where you want to enable monitoring.

Monitored projects

In the pom file of your projects add the common module dependency, download from exchange the latest api definitions and generate the flows with apikit.

Heartbeat

For the heartbeat flow it is enough to put a flow reference to the heartbeat subflow of the common module.

HealthCheck

Add a dataweave transform message as first processor in your healthcheck flow:

Prepare the input payload creating an array of objects where each object represent a dependency of the project

  • protocol: HTTP or HTTPS
  • serviceName: the name of the dependency (used only id the service does not reply)
  • host: the host of the dependency
  • port: the port of the dependency 
  • basepath: the base path of the dependency
  • path: can be “/heartbeat” or “/healthcheck” depending on how much you want to go deep in the dependencies tree
%dw 1.0
%output application/java
---
[
    {
        protocol:"HTTPS",
        serviceName:"dep_1_app_name",
        host:p("dep.1.host"),
        port:p("dep.1.port"),
        basepath:p("dep.1.base.path"),
        path:"/heartbeat"
    },
    {
        protocol:"HTTPS",
        serviceName:"dep_2_app_name",
        host:p("dep.2.host"),
        port:p("dep.2.port"),
        basepath:p("dep.2.path"),
        path:"/heartbeat"
    },
    {
        protocol:"HTTPS",
        serviceName:"dep_3_app_name",
        host:p("dep.3.host"),
        port:p("dep.3.port"),
        basepath:p("dep.3.path"),
        path:"/heartbeat"
    }
]

Add a flow reference after the transform message and reference this flow: “healthcheck_subflow

Anypoint Monitoring Configuration

Log in on Anypoint Portal and launch Anypoint Monitoring.

From the left menu select “Functional Monitoring” and press “Create”.

Configure the Monitor with your heartbeat/health check endpoint and a proper assertion

 

Finally choose the way you prefer to be notified in case of fail (Email, Slack, NewRelic, etc etc) and press “Create”.

Your monitor is now up & running and will periodically check the status of your apis.

Choose the right Location

Functional Monitor can be scheduled to run from internal or external Location. By Default each organization has a limited amount of external location available (5) while you can create as many internal location as needed, just keep in mind that internal location will use some of the cores of your organization license. For further details about the Monitor Location please che the official documentation

 

 

Author: Luca Piras