在微服务领域Spring Boot自动伸缩如何实现

标签: tuicool | 发表时间:2018-10-13 00:00 | 作者:
出处:http://itindex.net/relian

自动伸缩是每个人都想要的,尤其是在微服务领域。让我们看看如何在基于Spring Boot的应用程序中实现。

我们决定使用 KubernetesPivotal Cloud FoundryHashiCorp's Nomad等工具的一个更重要的原因是为了让系统可以自动伸缩。当然,这些工具也提供了许多其他有用的功能,在这里,我们只是用它们来实现系统的自动伸缩。乍一看,这似乎很困难,但是,如果我们使用 Spring Boot来构建应用程序,并使用 Jenkins来实现 CI,那么就用不了太多工作。

今天,我将向您展示如何使用以下框架/工具实现这样的解决方案:

  • Spring Boot

  • Spring Boot Actuator

  • Spring Cloud Netflix Eureka

  • Jenkins CI

它是如何工作的

每一个包含 Spring Boot Actuator库的 Spring Boot应用程序都可以在 /actuator/metrics端点下公开 metric。许多有价值的 metric都可以提供应用程序运行状态的详细信息。在讨论自动伸缩时,其中一些 metric可能特别重要: JVM、CPU metric、正在运行的线程数和HTTP请求数。有专门的 Jenkins流水线通过按一定频率轮询 /actuator/metrics端点来获取应用程序的指标。如果监控的任何 metric【指标】低于或高于目标范围,则它会启动新实例或使用另一个 Actuator端点 /actuator/shutdown来关闭一些正在运行的实例。在此之前,我们需要知道当前有那些实践在提供服务,只有这样我们才能在需要的时候关闭空闲的实例或启动新的新例。

在讨论了系统架构之后,我们就可以继续开发了。这个应用程序需要满足以下要求:它必须有公开的可以优雅地关闭应用程序和用来获取应用程序运行状态 metric【指标】的端点,它需要在启动完成的同时就完成在Eureka的注册,在关闭时取消注册,最后,它还应该能够从空闲端口池中随机获取一个可用的端口。感谢 Spring Boot,只需要约五分钟,我们可以轻松地实现所有这些机制。

动态端口分配

由于可以在一台机器上运行多个应用程序实例,所以我们必须保证端口号不冲突。幸运的是, Spring Boot为应用程序提供了这样的机制。我们只需要将 application.yml中的 server.port属性设置为 0。因为我们的应用程序会在 Eureka中注册,并且发送唯一的标识 instanceId,默认情况下这个唯一标识是将字段 spring.cloud.client.hostname, spring.application.nameserver.port拼接而成的。

示例应用程序的当前配置如下所示。

可以看到,我通过将端口号替换为随机生成的数字来改变了生成 instanceId字段值的模板。

spring:
  application:
    name: example-service
server:
  port: ${PORT:0}
eureka:
  instance:
    instanceId: ${spring.cloud.client.hostname}:${spring.application.name}:${random.int[1,999999]}

启用Actuator的Metric

为了启用 Spring Boot Actuator,我们需要将下面的依赖添加到 pom.xml

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

我们还必须通过HTTP API将属性 management.endpoints.web.exposure.include设置为 '*'来暴露 Actuator的端点。现在,所有可用的指标名称列表都可以在 /actuator/metrics端点中找到,每个指标的详细信息可以通过 /actuator/metrics/{metricName}端点查看。

优雅地停止应用程序

除了查看 metric端点外, Spring Boot Actuator还提供了停止应用程序的端点。然而,与其他端点不同的是,缺省情况下,此端点是不可用的。我们必须把 management.endpoint.shutdown.enabled设为 true。在那之后,我们就可以通过发送一个 POST请求到 /actuator/shutdown端点来停止应用程序了。

这种停止应用程序的方法保证了服务在停止之前从 Eureka服务器注销。

启用Eureka自动发现

Eureka是最受欢迎的发现服务器,特别是使用 Spring Cloud来构建微服务的架构。所以,如果你已经有了微服务,并且想要为他们提供自动伸缩机制,那么 Eureka将是一个自然的选择。它包含每个应用程序注册实例的IP地址和端口号。为了启用 Eureka客户端,您只需要将下面的依赖项添加到 pom.xml中。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

正如之前提到的,我们还必须保证通过客户端应用程序发送到 Eureka服务器的 instanceId的唯一性。在“动态端口分配”中已经描述了它。

下一步需要创建一个包含内嵌 Eureka服务器的应用程序。为了实现这个功能,首先我们需要在 pom.xml中添加下面这个依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

这个 main类需要添加 @EnableEurekaServer注解。

@SpringBootApplication
@EnableEurekaServer
public class DiscoveryApp {
    public static void main(String[] args) {
        new SpringApplicationBuilder(DiscoveryApp.class).run(args);
    }
}

默认情况下,客户端应用程序尝试使用 8761端口连接 Eureka服务器。我们只需要单独的、独立的 Eureka节点,因此我们将禁用注册,并尝试从另一个 Eureka服务器实例中获取服务列表。

spring:
  application:
    name: discovery-service
server:
  port: ${PORT:8761}
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

我们将使用 Docker容器来测试上面的自动伸缩系统,因此需要使用 Eureka服务器来准备和构建 image

Dockerfileimage的定义如下所示。

我们可以使用命令 docker build -t piomin/discovery-server:2.0来进行构建。

FROM openjdk:8-jre-alpine
ENV APP_FILE discovery-service-1.0-SNAPSHOT.jar
ENV APP_HOME /usr/apps
EXPOSE 8761
COPY target/$APP_FILE $APP_HOME/
WORKDIR $APP_HOME
ENTRYPOINT ["sh", "-c"]
CMD ["exec java -jar $APP_FILE"]

为弹性伸缩构建一个Jenkins流水线

第一步是准备 Jenkins流水线,负责自动伸缩。我们将创建 Jenkins声明式流水线,它每分钟运行一次。可以使用 triggers指令配置执行周期,它定义了自动化触发流水线的方法。我们的流水线将与 Eureka服务器和每个使用 Spring Boot Actuator的微服务中公开的 metric端点进行通信。

测试服务的名称是 EXAMPLE-SERVICE,它和定义在 application.yml文件 spring.application.name的属性值(大写字母)相同。被监控的 metric是运行在Tomcat容器中的HTTP listener线程数。这些线程负责处理客户端的HTTP请求。

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 { ... }
}

使用Eureka整合Jenkins流水线

流水线的第一个阶段负责获取在 discovery服务器上注册的服务列表。 Eureka发现了几个HTTP API端点。其中一个是 GET /eureka/apps/{serviceName},它返回一个给定服务名称的所有活动实例列表。我们正在保存运行实例的数量和每个实例 metric端点的URL。这些值将在流水线的下一个阶段中被访问。

下面的流水线片段可以用来获取活动应用程序实例列表。 stage名称是 Calculate。我们使用 HTTP请求插件来发起HTTP连接。

stage('Calculate') {
 steps {
  script {
   def response = httpRequest "http://192.168.99.100:8761/eureka/apps/${env.SERVICE_NAME}"
   def app = printXml(response.content)
   def index = 0
   env["INSTANCE_COUNT"] = app.instance.size()
   app.instance.each {
    if (it.status == 'UP') {
     def address = "http://${it.ipAddr}:${it.port}"
     env["INSTANCE_${index++}"] = address
    }
   }
  }
 }
}
@NonCPS
def printXml(String text) {
 return new XmlSlurper(false, false).parseText(text)
}

下面是 EurekaAPI对我们的微服务的示例响应。响应 content-typeXML

使用Spring Boot Actuator整合Jenkins流水线

Spring Boot Actuator使用 metric来公开端点,这使得我们可以通过名称和选择性地使用标签找到 metric。在下面可见的流水线片段中,我试图找到 metric低于或高于阈值的实例。如果有这样的实例,我们就停止循环,以便进入下一个阶段,它执行向下或向上的伸缩。应用程序的IP地址是从带有 INSTANCE_前缀的流水线环境变量获取的,这是在前一阶段中被保存了下来的。

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"
}

关闭应用程序实例

在流水线的最后一个阶段,我们将关闭运行的实例,或者根据在前一阶段保存的结果启动新的实例。通过调用 Spring Boot Actuator端点可以很容易执行停止操作。在接下来的流水线片段中,首先选择了 Eureka实例。然后我们将发送 POST请求到那个ip地址。

如果需要扩展应用程序,我们将调用另一个流水线,它负责构建 fat JAR并让这个应用程序在机器上跑起来。

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
  }
 }
}

下面是 spring-boot-run-pipeline流水线的完整定义,它负责启动应用程序的新实例。它先从 git仓库中拉取源代码,然后使用 Maven命令编译并构建二进制的jar文件,最后通过在 java -jar命令中添加 Eureka服务器地址来运行应用程序。

pipeline {
    agent any
    tools {
        maven 'M3'
    }
    stages {
        stage('Checkout') {
            steps {
                git url: 'https://github.com/piomin/sample-spring-boot-autoscaler.git', 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 &'
                }
            }
        }
    }
}

扩展到多个机器

在前几节中讨论的算法只适用于在单个机器上启动的微服务。如果希望将它扩展到更多的机器上,我们将不得不修改我们的架构,如下所示。每台机器都有 Jenkins代理运行并与 Jenkinsmaster通信。如果想在选定的机器上启动一个微服务的新实例,我们就必须使用运行在该机器上的代理来运行流水线。此代理仅负责从源代码构建应用程序并将其启动到目标机器上。这个实例的关闭仍然是通过调用HTTP端点来完成。

假设我们已经成功地在目标机器上启动了一些代理,我们需要对流水线进行参数化,以便能够动态地选择代理(以及目标机器)。

当扩容应用程序时,我们必须将代理标签传递给下游流水线。

build job:'spring-boot-run-pipeline', parameters:[string(name: 'agent', value:"slave-1")]

调用流水线具体由那个标签下的代理运行,是由" ${params.agent}"决定的。

pipeline {
    agent {
        label "${params.agent}"
    }
    stages { ... }
}

如果有一个以上的代理连接到主节点,我们就可以将它们的地址映射到标签中。由于这一点,我们能够将从 Eureka服务器获取的微服务实例的IP地址映射到与 Jenkins代理的目标机器上。

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"
        AGENT_192.168.99.102 = "slave-1"
        AGENT_192.168.99.103 = "slave-2"
    }
    stages { ... }
}

总结

在本文中,我演示了如何使用 Spring Boot Actuato metric来自动伸缩 Spring Boot应用程序。使用 Spring Boot提供的特性以及 Spring Cloud Netflix EurekaJenkins,您就可以实现系统的自动伸缩,而无需借助于任何其他第三方工具。本文也假设远程服务器上也是使用 Jenkins代理来启动新的实例,但是您也可以使用 Ansible这样的工具来启动。如果您决定从 Jenkins运行 Ansible脚本,那么将不需要在远程机器上启动 Jenkins代理。

相关 [微服务 领域 spring] 推荐:

在微服务领域Spring Boot自动伸缩如何实现

- - IT瘾-tuicool
自动伸缩是每个人都想要的,尤其是在微服务领域. 让我们看看如何在基于Spring Boot的应用程序中实现. 我们决定使用 Kubernetes、 Pivotal Cloud Foundry或 HashiCorp's Nomad等工具的一个更重要的原因是为了让系统可以自动伸缩. 当然,这些工具也提供了许多其他有用的功能,在这里,我们只是用它们来实现系统的自动伸缩.

微服务框架Spring Cloud介绍 Part2: Spring Cloud与微服务

- - skaka的博客
之前介绍过 微服务的概念与Finagle框架, 这个系列介绍Spring Cloud.. Spring Cloud还是一个相对较新的框架, 今年(2016)才推出1.0的release版本. 虽然Spring Cloud时间最短, 但是相比我之前用过的Dubbo和Finagle, Spring Cloud提供的功能最齐全..

微服务应用-基于Spring Cloud和Docker构建电影推荐微服务

- - CSDN博客推荐文章
使用Spring Cloud和Docker构建电影推荐微服务. 如果你对云应用很了解,可以直接移步下载运行项目(https://github.com/kbastani/spring-cloud-microservice-example),或跳转到部署步骤,. 本博客系列将向你介绍一些使用Spring Cloud和Docker构建微服务平台的基本概念.

Spring Cloud Netflix构建微服务入门实践

- - 简单之美
在使用Spring Cloud Netflix构建微服务之前,我们先了解一下Spring Cloud集成的Netflix OSS的基础组件Eureka,对于Netflix的其他微服务组件,像Hystrix、Zuul、Ribbon等等本文暂不涉及,感兴趣可以参考官网文档. 这里,我们用最基础的Eureka来构建一个最基础的微服务应用,来演示如何构建微服务,了解微服务的基本特点.

使用Spring Cloud和Docker构建微服务架构

- - Oopsguy
原文: https://dzone.com/articles/microservice-architecture-with-spring-cloud-and-do. 作者:Alexander Lukyanchikov. 如何使用Spring Boot、Spring Cloud、Docker和Netflix的一些开源工具来构建一个微服务架构.

Spring Cloud 微服务的那点事 - CSDN博客

- -
微服务的概念源于2014年3月Martin Fowler所写的一篇文章“Microservices”. 微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值. 每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API).

实用技巧:Spring Cloud中,如何优雅下线微服务?

- - 周立的博客 - 关注Spring Cloud、Docker
在生产环境中,服务的上下线是不可避免的,我们希望能够优雅地下线微服务. 本文基于Spring Boot 2.x + Spring Cloud Finchley讲解实际项目中优雅下线服务的四种方式,并探讨各方式的优缺点. 注:Spring Boot 1.x + Spring Cloud Edgware及之前的方式相同,但配置有区别,本文不做讨论.

Dubbo将积极适配Spring Cloud生态,Spring Cloud体系或将成为微服务的不二选择!

- - 程序猿DD
2016年,我在博客中发表过一篇 《微服务架构的基础框架选择:Spring Cloud还是Dubbo. 在这篇文章中,我主要对比了Spring Cloud与Dubbo所具备的能力,并阐述了个人推崇Spring Cloud的原因. 但是,最近各大技术社区出现了不少类似的文章,观点比较激进,对于Spring Cloud的褒扬远胜于Dubbo,但是这些评价很多都忽略了Spring Cloud与Dubbo在设计视角上的不同.

微服务架构的基础框架选择:Spring Cloud还是Dubbo?

- - 程序猿DD
最近一段时间不论互联网还是传统行业,凡是涉及信息技术范畴的圈子几乎都在讨论 微服务架构. 近期也看到各大技术社区开始组织一些沙龙和论坛来分享Spring Cloud的相关实施经验,这对于最近正在整理Spring Cloud相关套件内容与实例应用的我而言,还是有不少激励的. 目前,Spring Cloud在国内的知名度并不高,在前阵子的求职过程中,与一些互联网公司的架构师、技术VP或者CTO在交流时,有些甚至还不知道该项目的存在.

部署微服务的时候,Spring Cloud 和 Kubernetes 哪个更好? - DockOne.io

- -
【编者的话】本文比较了当下最火热的两大微服务开发平台:Spring Cloud和Kubernetes的优缺点,并指出结合二者的优点来组合使用,将在微服务旅程上获得更大成功. 当我们需要部署微服务的时候,哪个更好. Spring Cloud还是Kubernetes. 答案是都可以,只是各自有其优势. Kubernetes都宣称自己是开发和运行微服务的最佳环境,但是它们的本质非常不一样,所追求的目标也不同.