Auto-scaling is what everyone wants, especially in the microservices space. Let's look at how to do this in a Spring Boot-based application. One of the more important reasons we decided to use tools like Kubernetes, Pivotal Cloud Foundry, or HashiCorp's Nomad was to allow the system to scale automatically. Of course, these tools also provide a lot of other useful functionality, and we'll just use them here for automatic scaling. At first glance, this might seem difficult, but if we built the application using Spring Boot and implemented CI using Jenkins, it wouldn't take much work. Today, I'll show you how to implement such a solution using the following frameworks/tools:Copy the code

Spring Boot Spring Boot Actuator Spring Cloud Netflix Eureka Jenkins CI How does it work

Every Spring Boot application that contains the Spring Boot Actuator library can expose the metrics under the/Actuator /metrics endpoint. Many valuable metrics can provide detailed information about the health of your application. Some of these metrics can be particularly important when talking about automatic scaling: JVM, CPU metrics, number of running threads, and number of HTTP requests. There is a dedicated Jenkins pipeline that polls /actuator/metrics endpoints at a certain frequency to obtain application metrics. If any metric monitored is below or above the target range, it starts a new instance or uses another endpoint/Actuator /shutdown to shutdown some running instances. Before we do that, we need to know what practices are currently providing services so that we can shut down idle instances or start new ones as needed.Copy the code

Picture description (50 words Max)

After discussing the system architecture, we can move on to development. This application needs to meet the following requirements: It must have exposed endpoints for gracefully shutting down applications and getting application health metrics, it needs to register with Eureka at startup completion, unregister at shutdown, and finally, it should be able to randomly pick up an available port from the free port pool. Thanks to Spring Boot, we can easily implement all of these mechanisms in about five minutes.

Dynamic port allocation Since multiple application instances can run on a single machine, we must ensure that port numbers do not conflict. Fortunately, Spring Boot provides such a mechanism for applications. We just need to set the server.port property in application.yml to 0. Because our application would register with Eureka and send a unique instanceId, By default, this is a unique identifier field spring. Cloud. Client. The hostname, spring. Application. The name and server port of patchwork. The current configuration of the sample application is shown below. As you can see, I changed the template that generated the instanceId field value by replacing the port number with a randomly generated number.Copy the code

spring: application: name: example-service server: port: {spring.cloud.client.hostname}:{random.int[1,999999]} enables the Metric of the Actuator

To enable Spring Boot Actuator, we need to add the following dependencies to POM.xml.Copy the code
In addition to viewing metric endpoints, Spring Boot Actuator provides endpoints to stop applications gracefully. However, unlike other endpoints, this one is not available by default. We must get the management the endpoint. Shutdown. Enabled is set to true. After that, we can stop the application by sending a POST request to the /actuator/shutdown endpoint. This method of stopping the application ensures that the service is unregistered from the Eureka server before being stopped. Enabling Eureka automatic discovery Eureka is the most popular discovery server, especially the architecture that uses Spring Cloud to build microservices. So, if you already have microservices and want to provide automatic scaling for them, Eureka is a natural choice. It contains the IP address and port number of each application registered instance. To enable the Eureka client, you just need to add the following dependencies to pom.xml.Copy the code
The next step is to create an application that contains the embedded Eureka server. To implement this functionality, we first need to add the following dependency to pom.xml:Copy the code

@SpringBootApplication @EnableEurekaServer public class DiscoveryApp { public static void main(String[] args) { new SpringApplicationBuilder(DiscoveryApp.class).run(args); By default, the client application attempts to connect to the Eureka server using port 8761. We only need a separate, separate Eureka node, so we will disable registration and try to get the list of services from another Eureka server instance.

spring: application: name: discovery-service server: port: ${PORT:8761} eureka: instance: hostname: localhost client: registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://localhost:8761/eureka/, we will use the Docker container to test the automatic telescopic system above, so you need to use Eureka server to prepare and build the image.

Dockerfile and image are defined as follows. We can use the command docker build -t piomin/discovery-server:2.0 to build.Copy the code

FROM openJDK :8-jre-alpine ENV APP_FILE discovery-service-1.0-snapshot. jar ENV APP_HOME /usr/apps EXPOSE 8761 COPY target/APP_HOME/ WORKDIR APP_FILE”] builds a Jenkins pipeline for elastic scaling

The first step is to prepare the Jenkins pipeline, which is responsible for automatic scaling. We will create a Jenkins declarative pipeline that runs every minute. The execution cycle can be configured using the Triggers directive, which defines a way to automate the triggering pipeline. Our pipeline communicates with the Eureka server and the metric endpoints exposed in each of the micro-services using Spring Boot Actuators. The name of the test SERVICE is example-service, which is the same as the property value (uppercase) defined in the application.yml file spring.application.name. The metric being monitored is the number of HTTP Listener threads running in the Tomcat container. These threads are responsible for handling HTTP requests from clients.Copy the code

pipeline { agent any triggers { cron(‘* * * * *’) } environment { SERVICE_NAME = “EXAMPLE-SERVICE” METRICS_ENDPOINT = “/actuator/metrics/tomcat.threads.busy? tag=name:http-nio-auto-1” SHUTDOWN_ENDPOINT = “/actuator/shutdown” } stages { … }} Use Eureka to integrate Jenkins pipeline

The first phase of the pipeline is responsible for getting the list of services registered on the Discovery server. Eureka found several HTTP API endpoints. One of them is GET /eureka/apps/{serviceName}, which returns a list of all active instances of a given serviceName. We are saving the number of running instances and the URL of the Metric endpoint for each instance. These values will be accessed in the next stage of the pipeline. The pipeline fragment below can be used to get a list of active application instances. Stage is called Calculate. We use the HTTP request plug-in to initiate an HTTP connection.Copy the code

stage(‘Calculate’) { steps { script { def response = httpRequest “http://192.168.99.100:8761/eureka/apps/{it.ipAddr}:{index++}”] = address } } } } } @NonCPS def printXml(String text) { return new XmlSlurper(false, False).parseText(text)} Here is a sample Eureka API response to our microservice. The response Content-type is XML.

Picture description (50 words Max)

Spring Boot Actuators integrate Jenkins pipeline Spring Boot Actuators use metrics to expose endpoints, which allows us to find metrics by name and selectively using tags. In the pipeline fragment visible below, I try to find instances where metric is below or above the threshold. If there is such an instance, we stop the loop so that we can proceed to the next phase, which performs either downward or upward scaling. The application's IP address is obtained from the pipeline environment variable prefixed with INSTANCE_, which was saved in the previous phase.Copy the code

stage(‘Metrics’) { steps { script { def count = env.INSTANCE_COUNT for(def i=0; I 100) return “UP” else if (value.toINTEGER () < 20) return “DOWN” else return “NONE”} Closes the application instance

In the final phase of the pipeline, we either close the running instance or start a new one based on the results saved in the previous phase. Stopping operations can be performed easily by using Spring Boot Actuator endpoints. In the following pipeline fragment, the Eureka instance is selected first. Then we'll send a POST request to that IP address. If we need to extend the application, we will invoke another pipeline that builds the Fat JAR and gets the application running on the machine.Copy the code

stage(‘Scaling’) { steps { script { if (env.SCALE_TYPE == ‘DOWN’) { def ip = env[“INSTANCE_0”] + env.SHUTDOWN_ENDPOINT httpRequest url: ip, contentType: ‘APPLICATION_JSON’, httpMode: ‘POST’ } else if (env.SCALE_TYPE == ‘UP’) { build job: ‘spring-boot-run-pipeline’} currentbuild. description = env.scale_type}} It is responsible for starting a new instance of the application. It pulls the source code from the Git repository, compiles and builds the binary JAR file using the Maven command, and finally runs the application by adding the Eureka server address to the Java-jar command.

Pipeline {agent any tools {maven ‘M3’} stages {stage(‘Checkout’) {steps {git url: ‘github.com/piomin/samp… ‘, credentialsId: ‘github-piomin’, branch: ‘master’ } } stage(‘Build’) { steps { dir(‘example-service’) { sh ‘mvn clean package’ } } } stage(‘Run’) { steps { Dir (‘ example – service ‘) {sh ‘nohup Java jar – DEUREKA_URL = http://192.168.99.100:8761/eureka Target/example-service-1.0-snapshot. jar 1>/dev/null 2>logs/runlog &’}}}}} extends to multiple machines

The algorithms discussed in the previous sections only apply to microservices that are started on a single machine. If we wanted to scale it to more machines, we would have to modify our architecture, as shown below. Each machine has a Jenkins agent running and communicating with the Jenkins Master. If we want to start a new instance of the microservice on the selected machine, we must run the pipeline using an agent running on that machine. This agent is only responsible for building the application from source code and launching it on the target machine. Closing this instance is still done by calling the HTTP endpoint.Copy the code

Picture description (50 words Max)

Assuming that we have successfully started some agents on the target machine, we need to parameterize the pipeline so that the agents (and the target machine) can be selected dynamically. When scaling the application, we must pass the agent label to the downstream pipeline.Copy the code

build job:’spring-boot-run-pipeline’, parameters:[string(name: ‘agent’, value:”slave-1″)] the pipeline is run by the agent under the tag “${params.agent}”.

pipeline { agent { label “${params.agent}” } stages { … }} If there is more than one agent connected to the master node, we can map their address to the label. Because of this, we were able to map the IP address of the microservice instance obtained from the Eureka server to the target machine with the Jenkins agent.

pipeline { agent any triggers { cron(‘* * * * *’) } environment { SERVICE_NAME = “EXAMPLE-SERVICE” METRICS_ENDPOINT = “/actuator/metrics/tomcat.threads.busy? Tag =name: HTTP-nio-auto-1 “SHUTDOWN_ENDPOINT = “/actuator/shutdown” = “slave-1” agent_194.168.99.103 = “slave-2” } stages { … }} to summarize

In this article, I demonstrated how to use Spring Boot Actuato Metric to automatically scale a Spring Boot application. Using features provided by Spring Boot, along with Spring Cloud, Netflix, Eureka, and Jenkins, you can scale your system automatically without resorting to any other third-party tools. This article also assumes that the Jenkins agent is used to start new instances on the remote server, but you can also use a tool like Ansible to do so. If you decide to run Ansible scripts from Jenkins, you will not need to start the Jenkins agent on a remote machine.Copy the code

Welcome Java engineers who have worked for one to five years to join Java architecture development: 855835163 Group provides free Java architecture learning materials (which have high availability, high concurrency, high performance and distributed, Jvm performance tuning, Spring source code, MyBatis, Netty, Redis, Kafka, Mysql, Zookeeper, Tomcat, Docker, Dubbo, multiple knowledge architecture data, such as the Nginx) reasonable use their every minute and second time to learn to improve yourself, don’t use “no time” to hide his ideas on the lazy! While young, hard to fight, to the future of their own account!