Spring Boot: Custom Actuator Endpoints Creation

In the previous post , Spring Boot Actuator : : Monitor Health of Application you have learn about the Spring Boot actuator enabling and access health monitoring parameters through Spring Boot Actuator provided endpoints (default).

In this post, You will learn about to create custom Spring Boot Actuator end points. Sometimes these custom actuator endpoints required to get some specific information or customization of information instead of providing all detailed application health related information.

You have to follow these steps in Spring Boot application to create custom actuator endpoints.

Step 1:

Create or import any Spring Boot REST application. For Example using for User related Rest service (CRUD).

Step 2:

Enable the actuator in Spring Boot application by adding actuator starter in application pom.xml

<dependency>
	  <groupId>org.springframework.boot</groupId>
	  <artifactId>spring-boot-starter-actuator</artifactId>  
</dependency>

Step 3:

By default actuator allows external access for endpoints /actuator /health and /info, to access others custom actuator endpoints. you have add this property in your application.properties file.

management.endpoints.web.exposure.include=* 

Now, start your application and see the actuator is working fine or not by trying below url. If your server running on port 8090.

http://localhost:8090/actuator/health

{"status":"UP"}

UP status shows your application is running fine.

See More : Spring Boot Actuator : : Monitor Health of Application

Step 4:

Now to create custom actuator endpoints , you can create a class MyUserEndPoints in package com.facingIssuesOnIT.actuator as below:

To create a new endpoint you have to create a class and annotate it with @Component and @Endpoint annotation. The @Endpoint annotation has a parameter id (Ex: users) which determines the URL path of endpoint. This class contains the methods which returns the response of the endpoint. 

package com.y24y7.actuators;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import com.y24y7.model.User;
import com.y24y7.repository.UserRepository;

@Component
@Endpoint(id = "users")
public class MyUserEndpoints {
	
		@Autowired
		private UserRepository userRepository;
		
		@Autowired
		private Environment environment;
		
		@ReadOperation
		public List<User> getAllUsers(){
			List<User> users =  userRepository.findAll();			
			return users;
		}
		
		@WriteOperation
	    public String updateUser(Integer userId, String emailId) throws Exception {
		Optional<User> optional = userRepository.findById(userId);
			User user = optional.orElseThrow(() -> new Exception("Service.USER_NOT_FOUND"));
			user.setEmail(emailId);			
		return environment.getProperty("API.UPDATE_SUCCESS");
	}
		
		@DeleteOperation
		public String deleteUser(at2Selector Integer userId) {
			userRepository.deleteById(userId);
			return environment.getProperty("API.DELETE_SUCCESS");
		}		
} 

In above class,

  • The value of id parameter of @Endpoint is usersSo this endpoint is accessible by URL /actuator/users.
  • a method defined with @ReadOperation which will be mapped to HTTP GET method and automatically be exposed over HTTP.
  • a method defined with @WriteOperation which will be mapped to HTTP POST method and automatically be exposed over HTTP. The methods that are annotated with @WriteOperation can take parameters in JSON format alone.
  • a method defined with @DeleteOperation which will be mapped to HTTP DELETE method and automatically be exposed over HTTP.
  • technology-specific endpoints defined with @WebEndpoint/@JmxEndpoint. For ex, @WebEndpoint is exposed over HTTP only.

Step 5:

Restart the application and try with different custom actuator end points as below:

GET Method : http://localhost:8090/actuator/users

Response: Existing users list

Spring Boot Custom Actuator Endpoints : Get Method

POST Method : http://localhost:8090/actuator/users

Request Body

Spring Boot Actuator Endpoints : Post Method

It will create new user sunny with Id 345

Delete Method: http://localhost:8090/actuator/users/3

Spring Boot Actuator Endpoints : Delete Method

Here you have seen the custom actuator endpoints urls and response through the postman.

Conclusion

In this post you have learn about the below points:

  • Spring Boot Actuator configuration and enabling
  • Spring Boot custom actuator endpoints creation steps.
  • Spring Boot custom actuator endpoints access through Postman.

Let me know your thought on this post.

Happy Learning !!!

[Solved] OpenShift : MetaSpace Issue with SpringBoot based Micro-services

The java.lang.OutOfMemoryError: Metaspace indicates that allocated native memory for Java class metadata is exausted. That’s why issue occured in standalone and cloud based applications.

In Java 8 and later versions, the maximum amount of memory allocated for Java classes (MaxMetaspaceSize) is by default unlimited, so in most cases there is no need to change this setting. On the other hand, if you want to fix the amount of memory allocated for Java classes, you can set it as follows:

java -XX:MaxMetaspaceSize=1024m

This JVM parameter -XX:MaxMetaspaceSize is just an set the upper limit of MetaSpace. The current Metaspace size (i.e. committed) will be smaller. In fact, there is a setting called MaxMetaspaceFreeRatio (default 70%) which means that the actual metaspace size will never exceed 230% of its occupancy.

And for it to grow it first would have to fill up, forcing a garbage collection in an attempt to free objects and only when it cannot meet its MinMetaspaceFreeRatio (default 40%) goal it would expand the current metaspace. That can however not be greater than 230% of the occupancy after the GC cycle.

Monitoring MetaSpace Size with Java Native Memory tracking

A good way to monitor the exact amount of Metadata is by using the NativeMemoryTracking, which can be added through the following settings:

-XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=detail -XX:+PrintNMTStatistics

When Native Memory Tracking is enabled, you can request a report on the JVM memory usage using the following command:

$ jcmd <pid> VM.native_memory

OutOfMemoryError: Metaspace on OpenShift/Kubernetes

When using openjdk Image on OpenShift/Kubernetes, the default maxium value for the Metaspace is XX:MaxMetaspaceSize=100m. You might have noticed that setting this value through the JAVA_OPTIONS environment variable, doesn’t work as the default value is appended to the bottom:

VM Arguments: -Xms128m -Xmx1024m -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=256m    -XX:AdaptiveSizePolicyWeight=90 -XX:MaxMetaspaceSize=100m -XX:+ExitOnOutOfMemoryError

Once the MetaSpace get full in your application it will stop the service and through exception as below in your logs.

oc logs XYZ-service-7b856cc89-kpc6k  | grep -i metaspace
INFO exec  java -javaagent:/usr/share/java/jolokia-jvm-agent/jolokia-jvm.jar=config=/opt/jboss/container/jolokia/etc/jolokia.properties -XX:+UseParallelOldGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:MaxMetaspaceSize=100m -XX:+ExitOnOutOfMemoryError -cp "." -jar /deployments/XYZ-service-0.0.1-SNAPSHOT.jar
Picked up JAVA_TOOL_OPTIONS:  -Dappdynamics.agent.accountAccessKey=600a90af-582a-4ae2-87b1-4599708b65dd -Dappdynamics.agent.reuse.nodeName=true -Dappdynamics.socket.collection.bci.enable=true -XX:MaxMetaspaceSize=1024m -javaagent:/opt/appdynamics-java/javaagent.jar
Terminating due to java.lang.OutOfMemoryError: Metaspace

Solutions

The correct way to set the MaxMetaspaceSize is through the GC_MAX_METASPACE_SIZE environment variable. Here are the different cases ti implement this solutions:

  • Jenkins Pipeline: For example, if you are using a jenkins pipeline to deploy your application or services then you can meke following changes in the json template to refelect these changes in deployment.yaml file to deploy your application with JKube, the following settings will override the default values for the MaxMetaspaceSize and MaxMetaspaceSize:
spec:
  template:
    spec:
      containers:
      - env:
        - name: JAVA_OPTIONS
          value: '-Xms256m -Xmx1024m'
        - name: GC_MAX_METASPACE_SIZE
          value: 1024
        - name: GC_METASPACE_SIZE
          value: 256
  • Manual Deployment through S2I: You can directly pass these MetaSpace parameters while deploying your service manually.
oc new-app xyz-service -e JAVA_OPTIONS="-Xms256m -Xmx1024m" -e GC_MAX_METASPACE_SIZE=1024 -e GC_METASPACE_SIZE=256

Note : After deploying your service over OpenShift/Kernates validate the deployment configuration file(deployment.yml) for these parameters. In case not not reflecting then delete your pods completly and reploy the application.

In case, you want make changes on other parameters also for improving the puformance of your applictaion then you can follow these documents to list of parameters for OpenShift.

Let me know your thought on this post. if this solution was helpful for you make comment on the post.

Happy Learning !!!