在本文中,我将列出构建Spring Boot应用程序的“金科玉律”,这些应用程序是微服务系统一部分。这些“金科玉律”都来自我过往的经验,我曾经将运行在JEE服务器上的单体SOAP应用程序迁往基于REST的小型Spring Boot应用程序。这些最佳实践假设你的产品上已经拥有许多微服务,且每天要应对海量的请求。让我们开始吧。

收集度量指标

度量指标可视化可以改变组织中系统监控的方法,这非常令人惊讶。在Grafana中设置监控之后,我们能够识别系统中90%以上的大问题,避免这些问题在客户环境中发生并由客户提交给我们的支持团队。多亏了这两个带有大量图表和警报的监视器,我们的反应可能比以前快得多。如果你产品的体系架构是微服务而不是单体应用,那么度量指标会变得更加重要。
对我们来说,好消息是Spring Boot提供了收集最重要度量指标的内置机制。我们只需要设置一些配置文件来开启Spring Boot Actuator提供的预定义指标集。要使用Spring Boot Actuator,我们需要添加Spring Boot的启动依赖:

    org.springframework.boot    spring-boot-starter-actuator

要启用Spring Boot Actuator的功能,我们必须将management.endpoint.metrics设置为true。现在,你可以通过调用GET /actuator/metrics来检查度量指标的完整列表。对我们来说,最重要的指标之一是http.server.requests,它展示了请求数量和响应时间的统计信息,并会自动记录下请求类型(POST、GET等)、HTTP状态码和uri。
度量指标必须被存储在某个地方。最流行的工具是InfluxDBPrometheus。它们代表了两种不同的数据收集模型。Prometheus定期从应用程序的公开端点拉取数据,而InfluxDB提供了REST API,由应用程序来调用。Micrometer库提供了与InfluxDB、Prometheus和其他一些工具的集成方案。要启用对InfluxDB的支持,我们需要添加以下依赖项。

    io.micrometer    micrometer-registry-influx

我们还需要在Spring Boot的application.yml中配置InfluxDB的URL和数据库名。

management:metrics:    export:      influx:            db: springboot            uri: http://192.168.99.100:8086

要启用对Prometheus的支持,我们首先需要添加对应的Micrometer库,然后设置属性management.endpoint.metrics为true。

    io.micrometer    micrometer-registry-prometheus

默认情况下,Prometheus每分钟尝试从定义的目标端点收集一次数据。可以在Prometheus的配置文件中设置这些参数,scrape_config部分负责指定一组目标和参数,以及如何与它们连接。

scrape_configs:- job_name: 'springboot'    metrics_path: '/actuator/prometheus'    static_configs:    - targets: ['person-service:2222']

有时候,为指标数据打上标签是非常有用的,特别是当我们在单个微服务系统中存在多个应用程序,并将指标数据发送给单个Influx数据库时。下面是代码示例。

@Configurationclass ConfigurationMetrics {    @Value("${spring.application.name}")    lateinit var appName: String    @Value("${NAMESPACE:default}")    lateinit var namespace: String    @Value("${HOSTNAME:default}")    lateinit var hostname: String    @Bean    fun tags(): MeterRegistryCustomizer {            return MeterRegistryCustomizer { registry ->                    registry.config().commonTags("appName", appName).commonTags("namespace", namespace).commonTags("pod", hostname)            }    }} 

这是一张从Grafana中截取的监控视图,表示单个应用的http.server.requests指标。

不要忘记日志

日志记录在开发过程中不是很重要,但在维护过程中却非常重要。值得记住的是,在组织中,通过日志的质量可以间接判断应用程序的质量。通常,应用程序由支持团队维护,因此日志应该是非常重要的。不要试图把所有的事情都放在日志里,只有最重要的事件才应该被记录下来。
为微服务中的所有应用程序使用相同的日志格式也很重要。例如,如果要以JSON格式记录日志,那么对每个应用程序都应该用JSON格式。如果你使用标记appName来指示应用程序的名称或用instanceId来区分同一应用程序的不同实例,那么在任何地方都要这样做。为什么?你通常希望将微服务中收集的所有日志存储在一个单独的中央数据库中。最流行的工具(或者说是收集日志的工具)是Elastic Stack(ELK)。将中央数据库的优势发挥到最大,你应该确保查询条件和响应结构对于所有应用程序都是相同的,尤其是将不同微服务之间的日志关联起来。怎么做呢?当然是通过使用第三方库。我推荐你使用Spring Boot logging,要使用它,你需要添加如下依赖。

com.github.piominlogstash-logging-spring-boot-starter1.2.2.RELEASE

这个库将迫使你使用一些良好的日志记录实践,并自动与Logstash集成(ELK中负责收集日志的工具)。它的主要特点是:

  • 能够记录完整的HTTP请求和HTTP响应,并使用适当的标签将这些日志发送到Logstash,这些标签标明了HTTP的请求类型和HTTP响应码
  • 计算和存储每个请求的执行时间
  • 当使用Spring RestTemplate调用的下游服务时,生成和传递correlationId

为了能够向Logstash发送日志,我们至少需要如下配置信息。

logging.logstash:enabled: trueurl: 192.168.99.100:5000

在添加了logstash-logging-spring-boot-starter之后,你就可以使用Logstash中的日志标记功能。下图是来自Kibana中的单条日志记录的截图。

我们还可以将库Spring Cloud Sleuth添加到我们的依赖项中。

     org.springframework.cloud    spring-cloud-starter-sleuth

Spring Cloud Sleuth传递与Zipkin(一种流行的分布式跟踪工具)兼容的标头。它的主要特点是:

  • 它将跟踪(相关请求)和span IDs添加到Slf4J MDC
  • 它记录时间信息以帮助进行延迟分析
  • 它修改了日志的格式,以添加一些信息,比如附加的MDC字段
  • 它提供与其他Spring组件的集成,如OpenFeign、RestTemplate或Spring Cloud Netflix Zuul

让你的API易用

在大多数情况下,其他应用程序将通过REST API调用你的应用程序。因此我们需要小心的维护一份API文档。文档应该由代码生成。当然有一些工具可以做到这一点。其中最受欢迎的是Swagger。你可以使用SpringFox项目轻松地将Swagger 2集成到你的Spring Boot应用程序中。为了使用Swagger,我们需要添加如下依赖。第一个库负责从Spring MVC控制器代码中生成Swagger descriptor,而第二个库负责解析Swagger descriptor并在你的浏览器中展示页面。

    io.springfox    springfox-swagger2    2.9.2    io.springfox    springfox-swagger-ui    2.9.2

我们还必须提供一些Bean来修改Swagger的默认行为。它应该只展示我们自己提供的REST API,而不展示Spring Boot Actuator的API。我们也可以通过定义UiConfiguration bean来定制UI外观。

@Configuration@EnableSwagger2public class ConfigurationSwagger {    @Autowired    Optional build;    @Bean    public Docket api() {            String version = "1.0.0";            if (build.isPresent())                    version = build.get().getVersion();            return new Docket(DocumentationType.SWAGGER_2)                            .apiInfo(apiInfo(version))                            .select()                            .apis(RequestHandlerSelectors.any())                            .paths(PathSelectors.regex("(/components.*)"))                            .build()                            .useDefaultResponseMessages(false)                            .forCodeGeneration(true);    }    @Bean    public UiConfiguration uiConfig() {            return UiConfigurationBuilder.builder().docExpansion(DocExpansion.LIST).build();    }    private ApiInfo apiInfo(String version) {            return new ApiInfoBuilder()                            .title("API - Components Service")                            .description("Managing Components.")                            .version(version)                            .build();    }} 

下图是Swagger 2 UI的例子。

接下来是为所有微服务定义相同的REST API准则。如果你始终如一地为你的微服务构建API,那么对于外部和内部客户端,集成微服务要简单得多。指南应该包含如何构建API的说明,需要在请求和响应上设置哪些HTTP header,如何生成错误码等。这些准则应该与组织中的所有开发人员和供应商共享。有关为Spring Boot微服务生成Swagger文档(包括为API网关上的所有应用程序公开它)的更详细说明,您可以参考我的文章《Microservices API Documentation with Swagger2》。

不要害怕使用断路器

如果你使用Spring cloud在微服务之间进行通信,你可以利用Spring Cloud Netflix Hystrix或Spring Cloud断路器来实现断路。然而,由于Netflix不再开发Hystrix,Pivotal团队已经将第一个解决方案转移到了维护模式。推荐的解决方案是构建在resilience4j项目之上的新的Spring Cloud断路器。

    org.springframework.cloud    spring-cloud-starter-circuitbreaker-resilience4j

然后,我们需要为断路器设置所需的配置,方法是定义一个Customizer bean,该bean被传递给Resilience4JCircuitBreakerFactory。以下示例使用默认值。

@Beanpublic Customizer defaultCustomizer() {    return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)                    .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(5)).build())                    .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())                    .build());} 

有关将Hystrix断路器与Spring Boot应用程序集成的详细信息,请参阅我的另一篇文章:《Part 3: Creating Microservices: Circuit Breaker, Fallback and Load Balancing with Spring Cloud》。

使应用程序透明

我们不应该忘记,迁移到微服务的最重要原因之一是持续交付的需求。今天,快速交付变更的能力在市场上具有优势。你甚至应该能够在一天内多次交付更改。因此,重要的是当前是什么版本,它在哪里发布,以及它包括哪些更改。
在使用Spring Boot和Maven时,我们可以很容易地发布诸如最后更改日期、Git的commit id或应用程序的多个版本等信息。要实现这一点,我们只需要在我们的pom.xml中包含以下Maven插件。

                org.springframework.boot            spring-boot-maven-plugin                                                                                                build-info                                                                                pl.project13.maven            git-commit-id-plugin                                false                

假设你已经启用了Spring Boot Actuator(参见第1节),你必须启用/info端点来显示所有有趣的数据。

management.endpoint.info.enabled: true

当然,我们有许多微服务组成的一个大系统,并且每个微服务都有一些正在运行的实例。最好是在一个应用程序中监视我们所有的实例——就像收集度量指标和收集日志一样。幸运的是,有一个专门用于Spring Boot应用程序的工具,它能够从所有Actuator端点收集数据并在UI中显示它们。它是由Codecentric开发的Spring Boot Admin。使用Spring Boot Admin最方便的方式是创建一个专门的Spring Boot应用程序,该程序需要添加Spring Boot Admin的依赖项和服务发现的依赖项,例如Spring Cloud Netflix Eureka。

    de.codecentric    spring-boot-admin-starter-server    2.1.6    org.springframework.cloud    spring-cloud-starter-netflix-eureka-client

然后,我们应该通过使用@EnableAdminServer注解来启用Spring Boot Admin。

@SpringBootApplication@EnableDiscoveryClient@EnableAdminServer@EnableAutoConfigurationpublic class Application {    public static void main(String[] args) {            SpringApplication.run(Application.class, args);    }} 

通过Spring Boot Admin,我们可以轻松地浏览在服务发现中注册的应用程序列表,并检查每个应用程序的版本或提交信息。

我们可以点击“Details”按钮查看更多详细信息,这些信息都来自于/info端点和从其他Actuator端点收集到的数据。

编写合同测试

消费者驱动契约(Consumer Driven Contract,CDC)测试是一种方法,它允许你验证系统内应用程序之间的集成。系统内部的集成数量可能非常大,尤其是在维护基于微服务的系统时。由于Spring Cloud Contract项目的存在,在Spring Boot中进行契约测试会相对容易一些。还有一些专门为CDC设计的框架,如Pact,但Spring Cloud Contract可能是首选,因为我们使用的是Spring Boot。
要在生产者端使用Spring Cloud Contract,我们需要添加Spring Cloud Contract Verifier。

    org.springframework.cloud    spring-cloud-starter-contract-verifier    test

在消费者端,我们需要添加Spring Cloud Contract Stub Runner。

    org.springframework.cloud    spring-cloud-starter-contract-stub-runner    test

第一步是定义契约。编写它的一种方法是使用Groovy语言。合同应在生产者和消费者双方进行核实。例如:

import org.springframework.cloud.contract.spec.ContractContract.make {    request {            method 'GET'            urlPath('/persons/1')    }    response {            status OK()            body([                    id: 1,                    firstName: 'John',                    lastName: 'Smith',                    address: ([                            city: $(regex(alphaNumeric())),                            country: $(regex(alphaNumeric())),                            postalCode: $(regex('[0-9]{2}-[0-9]{3}')),                            houseNo: $(regex(positiveInt())),                            street: $(regex(nonEmpty()))                    ])            ])            headers {                    contentType(applicationJson())            }    }} 

契约与存根一起打包在JAR中。它可以发布到像Artifactory或Nexus这样的存储库管理器,然后消费者可以在JUnit测试期间从存储库管理器下载契约。生成的JAR文件以存根作为后缀。

@RunWith(SpringRunner.class)@SpringBootTest(webEnvironment = WebEnvironment.NONE)@AutoConfigureStubRunner(ids = {"pl.piomin.services:person-service: :stubs:8090"}, consumerName = "letter-consumer",  stubsPerConsumer = true, stubsMode = StubsMode.REMOTE, repositoryRoot = "http://192.168.99.100:8081/artifactory/libs-snapshot-local")@DirtiesContextpublic class PersonConsumerContractTest {    @Autowired    private PersonClient personClient;    @Test    public void verifyPerson() {            Person p = personClient.findPersonById(1);            Assert.assertNotNull(p);            Assert.assertEquals(1, p.getId().intValue());            Assert.assertNotNull(p.getFirstName());            Assert.assertNotNull(p.getLastName());            Assert.assertNotNull(p.getAddress());            Assert.assertNotNull(p.getAddress().getCity());            Assert.assertNotNull(p.getAddress().getCountry());            Assert.assertNotNull(p.getAddress().getPostalCode());            Assert.assertNotNull(p.getAddress().getStreet());            Assert.assertNotEquals(0, p.getAddress().getHouseNo());    }} 

契约测试不会验证微服务系统中的复杂用例。然而,这是测试微服务之间集成的第一阶段。一旦确保了应用程序之间的API契约是有效的,就可以进行更高级的集成或端到端测试。关于持续集成与Spring Cloud Contract的更详细解释,可以参考我的另一篇文章《Continuous Integration with Jenkins, Artifactory and Spring Cloud Contract》。

更新到最新版本

Spring Boot和Spring Cloud相对频繁地发布新版本的框架。假设您的微服务的代码数量不是很多,那么很容易的就可以升级到最新版本。Spring Cloud使用release train模式发布项目的新版本,以简化依赖关系管理并避免库的不兼容版本之间的冲突问题。
此外,Spring Boot系统地改善了应用程序的启动时间和内存占用,因此值得对其进行更新。这是Spring Boot和Spring Cloud当前的稳定版本。

    org.springframework.boot    spring-boot-starter-parent    2.2.1.RELEASE                                    org.springframework.cloud                    spring-cloud-dependencies                    Hoxton.RELEASE                    pom                    import                

结论

我向你展示了如何使用Spring Boot的特性和Spring Cloud的一些附加库,作为最佳实践这并不困难。这些最佳实践将使您更容易地迁移到基于微服务的体系结构中,并在容器中运行应用程序。

springboot 微服务_Spring Boot在微服务中的最佳实践相关推荐

  1. 微前端在平台级管理系统中的最佳实践

    微前端在平台级管理系统中的最佳实践 一.什么是微前端 二.什么是通用管理端工程 三.当管理端工程遇上微前端 四.未来展望 作者:杨朋飞 一.什么是微前端 近十年来,前端技术有了长足发展,各种概念与框架 ...

  2. springboot jar服务器运行后无法请求_Spring boot、微服务、OAuth、OpenID的爱恨情仇!...

    在本文中,我们学习如何使用Spring boot轻松配置和部署微服务,然后使用OAuth和OpenID保护它们. 在微服务体系架构中,其中较大的应用程序由多个较小的服务组成,每个服务都有自己的目标,它 ...

  3. springboot 优雅停机_Spring boot 2.3优雅下线,距离生产还有多远?

    简介:对于任何一个线上应用,如何在服务更新部署过程中保证业务无感知是开发者必须要解决的问题,即从应用停止到重启恢复服务这个阶段不能影响正常的业务请求,这使得无损下线成为应用生命周期中必不可少的一个环节 ...

  4. springboot 后台模板_spring boot实战

    亲们,我们今天来看看spring boot.如果你掌握了Servlet.JSP.Filter.Listener等web开发技术,掌握了Spring MVC.Spring.Mybatis框架的使用,掌握 ...

  5. springboot tomcat配置_Spring Boot项目如何同时支持HTTP和HTTPS协议

    本文首发于个人网站:Spring Boot项目如何同时支持HTTP和HTTPS协议 如今,企业级应用程序的常见场景是同时支持HTTP和HTTPS两种协议,这篇文章考虑如何让Spring Boot应用程 ...

  6. Spring Boot 最流行的 16 条最佳实践!

    点击上方 IT牧场 ,选择 置顶或者星标技术干货每日送达! 来源:http://t.cn/EJWZNra Spring Boot是最流行的用于开发微服务的Java框架.在本文中,我将与你分享自2016 ...

  7. 命令行调用springboot服务_Spring Boot命令行界面的使用

    Spring Boot提供了命令行界面(Command Line Interface, CLI),可以用来运行和测试Spring Boot应用.Spring Boot及其CLI可以在Spring仓库中 ...

  8. feign调用多个服务_spring cloud各个微服务之间如何相互调用(Feign、Feign带token访问服务接口)...

    1.首先先看什么是Feign. 2.若其他服务的接口未做权限处理,参照上文第1点的博文即可. 3.若其他服务的接口做了权限的处理(例如OAuth 2)时该如何访问? a.有做权限处理的服务接口直接调用 ...

  9. Java注解配置rest服务_Spring Boot 注解—常用注解

    注:该部分内容包含一些常用注解,如果没有学习过java注解的同学可以先看一下上一小节的内容Spring Boot 注解-基本知识 ,不看也没关系,下面就开始本节内容. @Configuration注解 ...

  10. springboot aop使用_Spring Boot 的自动配置,是如何实现的?

    点击上方"IT牧场",选择"设为星标"技术干货每日送达! 作者 | 祖大帅 链接 | juejin.im/post/5b679fbc5188251aad2131 ...

最新文章

  1. ​Openresty最佳案例 | 第8篇:RBAC介绍、sql和redis模块工具类
  2. ubuntu java ide,ubuntu 环境 安装 C++ IDE Eclipse
  3. 一起来看流星雨剧情简介/剧情介绍/剧情分集介绍第六集
  4. CTS(16 )---Android 兼容性定义文档
  5. Httpd2.4简介及CenOS6.6下编译安装
  6. ios URLSchemes
  7. 我给一个团队新成员的信
  8. 容器技术Docker K8s 1 云原生技术概述
  9. 基于deap数据集的脑电情感分析(matlab代码)
  10. 知到智慧树答案2020python_2020知到智慧树大数据分析的python基础章节答案
  11. windows无法格式化u盘_U盘打不开?无法格式化?试试这个方法可能有效!
  12. COJ 0358 xjr考考你数据结构(根号3)线段树区间修改
  13. python 使用PIL工具包中的pytesseract函数识别英文字符
  14. SURF(Speeded Up Robust Features)算法原理
  15. 01-java设计模式知识点-上篇
  16. 【机器学习】机器学习公共数据集整理
  17. vivo手机可以适配鸿蒙系统吗,originos系统适配机型 vivo全新手机系统支持哪些机型...
  18. elementUI分页删除最后一页数据时跳转回上一页并且el-table序号递增
  19. Markdown KaTex 积分符号
  20. Mybatis小知识_typeAliases/typeAliases标签作用

热门文章

  1. LIO-SAM探秘第二章之安装编译与参数配置
  2. k均值聚类+基于核的k均值聚类+C++实现
  3. 深度学习实现工业零件的缺陷检测
  4. arbotix导入出错
  5. Mysql添加远程访问权限
  6. 第五次作业11111
  7. mysql忘记root密码怎么办,几个简单操作步骤找回mysql密码
  8. 【转】Android 平台下使用 i2c-tools
  9. 生命在此定格 路透记者遇难前拍下的最后画面
  10. Css技巧:模块化编码