vlambda博客
学习文章列表

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

Chapter 14. Developer Tools for Spring Boot Apps

我欠@springboot 很多。 #productivity #engineering #sota #minimalist #microservices #performance #quality #bestpractices

Amir Sedighi @amirsedighi

在上一章中,您学习了如何使用 Spring Boot 的各种测试功能。我们看到了如何制作简单的单元测试、切片测试、模拟 WebFlux 测试,甚至是完全旋转的嵌入式 Netty 集成测试。

当我们步入正轨时,任何能够改变构建应用程序所花费时间曲线的东西都会受到赞赏。我们将探索 Spring Boot 带来的各种工具,以帮助我们破解我们的应用程序。

在本章中,我们将执行以下操作:

  • Using Spring Boot's DevTools for hot code reloading and decaching
  • Glean what Spring Boot did with its autoconfiguration report
  • Make local changes and see them on the target system
  • Write a custom health check
  • Add build data to the /application/info endpoint
  • Create custom metrics

Using Spring Boot's DevTools for hot code reloading


开发人员总是在寻找加速 事情了。很久以前,最大的加速之一是增量编译器,并且每次我们保存文件时都运行它们。现在它已渗透 现代工具,没有人会三思而后行。

在构建 Spring Boot 应用程序时,最需要的是检测代码更改并重新启动嵌入式容器的能力。

值得庆幸的是,我们只需要在上一章构建的代码中添加一个内容:

    compile("org.springframework.boot:spring-boot-devtools") 

Note

如果你碰巧在使用 Maven,你会想要包含 optional 标志。

因此,这个微型模块执行以下活动:

  • Disables cache settings for autoconfigured components
  • When it detects a change in code, it restarts the application, holding onto third-party classes and simply throwing away and reloading custom classes
  • Activates an embedded LiveReload (http://livereload.com/) server that can trigger the browser to refresh the page automatically

有关所有禁用组件的列表,请查看以下代码片段:

    properties.put("spring.thymeleaf.cache", "false"); 
    properties.put("spring.freemarker.cache", "false"); 
    properties.put("spring.groovy.template.cache", "false"); 
    properties.put("spring.mustache.cache", "false"); 
    properties.put("server.session.persistent", "true"); 
    properties.put("spring.h2.console.enabled", "true"); 
    properties.put("spring.resources.cache-period", "0"); 
    properties.put("spring.resources.chain.cache", "false");
    properties.put("spring.template.provider.cache", "false"); 
    properties.put("spring.mvc.log-resolved-exception", "true"); 
    properties.put("server.servlet.jsp.init-parameters.development",
     "true"); 
    properties.put("spring.reactor.stacktrace-mode.enabled", "true"); 

Note

当应用程序在调试模式下运行时,许多 IDE 还提供额外的重新加载支持,这是与 Spring Boot DevTools 结合使用的强烈推荐选项。

你问净收益是多少?

当我们对代码进行更改并发出 SaveMake Project ,DevTools 将丢弃保存我们自定义代码的类加载器并启动一个新的应用程序上下文。这使得重新启动相对较快。

Note

保存或制作项目? Spring Boot DevTools 监听文件更新。对于某些 IDE,例如 Eclipse,⌘-S 用于执行 Save 操作。 IntelliJ IDEA 会自动保存,因此另一个信号是 Make Project,⌘-F9,它会刷新环境。

在 LiveReload 服务器运行和 LiveReload 插件(http://livereload.com/extensions/) 安装在我们的浏览器中,我们可以在访问该站点时启用 LiveReloading。任何时候我们 更新代码,插件基本上都会为我们点击浏览器的刷新按钮。

Note

Restarting vs reloading:DevTools 提供了重新启动应用程序的能力很快,但它在各种方面受到限制。例如,不会选择通过添加新依赖项来更新类路径。不支持添加新类。对于处理这些复杂用例的更复杂的工具,您可能希望研究诸如 Spring Loaded (https://github.com/spring-projects/spring-loaded) 或 JRebel (http://zeroturnaround.com/software/jrebel/)。

 

清除所有这些缓存后,我们可以看到更改传播much 更快。让我们在调试模式下启动 LearningSpringBootApplication 来测试一下。如果我们访问该站点,事情看起来和预期的一样:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

该站点从我们预加载的测试数据开始。要让浏览器监听更新,我们需要点击 LiveReload 图标:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

起初,中心的点是空心的。启用后,圆点变为实心,如前面的屏幕截图所示。

让我们对模板进行一些编辑:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

有了这个额外的子标题,我们只需要在 IDE 中点击 Save 或 Make Project。切换到浏览器将立即显示结果:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

让我们对 HomeController 做一些调整,如下所示:

    @GetMapping("/") 
    public Mono<String> index(Model model) { 
      model.addAttribute("images", 
        imageService.findAllImages()); 
      model.addAttribute("extra", 
        "DevTools can also detect code changes too"); 
      return Mono.just("index"); 
    } 

这与上一章相同,只是我们在模型中添加了一个新属性 extra。我们可以通过调整模板来显示它:

    <h4 th:text="${extra}"></h4> 

这会将新的 extra 模型属性显示为 H4 标头,所有这些都无需在浏览器中单击任何内容:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

使用 Spring Boot DevTools for 重新启动——任何内存中的数据或状态都将丢失。

这可能是好是坏。它当然鼓励您创建一个预加载器,可能带有 @Profile("dev") 注释,以便它仅在 spring 时运行。 profile.active=dev 已打开。

如果我们的用例需要很多步骤来设置,这可能会成为一个问题,并且重新启动应用程序会使我们一次又一次地重复这些步骤。内存数据库解决方案(例如 H2)放大了这一点。在我们的情况下,清除上传文件的启动代码会导致类似的数据刷新。

考虑不在浏览器中打开 LiveReload(但让应用程序重新启动)的另一个原因是,如果我们正在处理大量 JavaScript 的前端并且不希望每次更改都强制重新加载。例如,我们可能有一个填写了很多字段的页面。触发的重启可能会清除我们的表单并迫使我们重新输入数据。

尽管如此,这是一个很好的问题。具有 选项来刷新 浏览器并与代码更改保持同步是一个强大的工具。

Using Spring Boot's autoconfiguration report


正如我们目前在本书中看到的那样,Spring 引导自动配置 bean 以帮助我们避免配置基础设施,而是专注于编码业务需求。但是,有时,我们可能想知道 Spring Boot 为我们做了(或没有)做什么。

这就是为什么它有一个自动配置报告。本质上,每次基于 on 一些条件检查选择 bean 时,Spring Boot 都会记录决定(是或否)并提供它以许多不同的方式带给我们。

最简单的方法是将 --debug 添加到运行配置中。在下面的截图中,我们可以看到如何在 IntelliJ 中设置它:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

如果我们使用 --debug 作为程序参数启动我们的应用程序,则会将自动配置报告打印到控制台:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

这很好,在某些故障情况下,报告将自动打印出来以帮助进行事后分析。但是,从控制台获取报告并不是很有效。

如果我们使用 Spring Boot Actuator,我们可以在 in 更好的 JSON 结构。如果您还记得,我们在 Actuator ch10">第 10 章Java 快速入门

    compile('org.springframework.boot:spring-boot-starter-actuator')  

Note

如果您正在构建一个新应用程序并且没有http://start.spring.io,这个依赖很有价值。

除了添加 Spring Boot的Actuator模块,我们还必须选择加入< /em> 或者启用它的功能。在 Spring Boot 2.0 中,Actuator 支持多种技术,包括 Spring MVC、Spring WebFlux 和 JMX。我们必须指出我们希望启用哪些平台,而不是期望 Spring Boot 猜测。为此,我们需要将以下代码行添加到我们的 application.properties 文件中:

    endpoints.default.web.enabled=true

从 HTTP 的角度来看,这将使 Actuator 的端点处于活动状态; (要为 JMX 启用执行器,我们需要将 endpoints.default.jmx.enabled 设置为 true)。

当我们启动我们的应用程序时,会添加几个 Spring WebFlux 端点,提供额外的信息。要快速浏览 all 可用的端点,我们可以访问 http ://localhost:8080/application,如下图所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

此屏幕截图并未全部捕获,但有一长串端点提供有关我们应用程序的详细信息。 (顺便说一下,在本书的前面,我们只启用了一个执行器端点,/application/health。这个标志让我们打开all 默认端点。)

从那里,我们可以在 http://localhost:8080/application/autoconfig 轻松找到自动配置报告,点击它,感谢 JSON查看器(https://github.com/tulios/json-viewer ),请参阅此 nicely 格式的报告:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

好的,我们已经看到了几种生成此报告的方法。但它是什么意思?

如果我们放大一个片段,我们可以找出 something

    "ReactiveWebServerConfiguration.ReactorNettyAutoConfiguration": [ 
    { 
      "condition": "OnClassCondition", 
      "message": "@ConditionalOnClass found required class 
      'reactor.ipc.netty.http.server.HttpServer'; 
      @ConditionalOnMissingClass did not find unwanted class" 
    }, 
    { 
      "condition": "OnBeanCondition", 
      "message": "@ConditionalOnMissingBean (types: 
        org.springframework.boot.web.reactive
        .server.ReactiveWebServerFactory; SearchStrategy: all) did
         not find any beans" 
    } 
    ] 

自动配置报告中的 JSON 片段可以描述如下:

  • ReactiveWebServerConfiguration.ReactorNettyAutoConfiguration is a Spring Boot autoconfiguration policy that was evaluated, specifically on the subject of Netty.
  • @ConditionalOnClass matched on spotting Reactor's HttpServer class, glue code used by Reactor to embed the Netty container. This shows that Netty was on the classpath.
  • @ConditionalOnMissingBean is the second condition, and was negative, indicating there is no overriding, user-defined ReactiveWebServerFactory defined. Therefore, Spring Boot is activating its default policy for Reactor Netty.

要准确预测这个自动配置策略是什么,我们可以打开代码并自己检查。使用我们的 IDE,我们只需要查找父类 ReactiveWebServerConfiguration

    abstract class ReactiveWebServerConfiguration { 
 
      @ConditionalOnMissingBean(ReactiveWebServerFactory.class) 
      @ConditionalOnClass({ HttpServer.class }) 
      static class ReactorNettyAutoConfiguration { 
 
        @Bean 
        public NettyReactiveWebServerFactory 
         NettyReactiveWebServerFactory() { 
           return new NettyReactiveWebServerFactory(); 
        } 
 
      } 
      ... 
    } 

这个来自 Spring Boot 的 Reactive web server configuration 代码的片段可以解释如下:

  • ReactiveWebServerConfiguration is an abstract class that is merely used as a container for other policies
  • @ConditionalOnMissingBean(ReactiveWebServerFactory.class) tells Spring Boot to back off and not use this if the user has declared such a bean elsewhere
  • @ConditionalOnClass({HttpServer.class}) tells Spring Boot to only consider this if Reactor Netty is on the classpath
  • static class ReactorNettyAutoConfiguration names this rule used to autoconfigure Reactor Netty
  • @Bean flags the code as a Spring bean
  • return new NettyReactiveWebServerFactory() actually creates the Spring bean for Reactor Netty

所有这些结合在一起,允许在放入类路径时自动配置 Reactor Netty。我们在自动配置报告中发现了它。

Note

ReactiveWebServerConfiguration 中还有其他 bean 定义,包括对 Jetty、Apache Tomcat 和 Undertow 的支持,但由于篇幅限制未显示。

这有什么用?

如果我们尝试使用 Spring Boot 的某些功能,它不会按预期进行,要调试的一件事是是否正在创建预期的 bean。另一种用法是,如果我们正在为给定项目开发自己的自动配置模块,并且需要查看是否正在创建正确的 bean。

你看,自动配置报告并不局限于 Spring 团队发布的内容。它着眼于一切

说到做出改变,请注意我们是如何让 Netty 在后台运行的。我们可以从控制台输出以及我们刚刚查看的自动配置报告中得知。

如果我们想更换容器怎么办?使用 Spring Boot 非常容易。我们只需要调整构建文件。

默认情况下,Spring Boot 将 Netty 用于 Reactive 应用程序,但切换起来并不难:

    compile('org.springframework.boot:spring-boot-starter-webflux') { 
      exclude module: 'spring-boot-starter-reactor-netty' 
    } 
    compile('org.springframework.boot:spring-boot-starter-undertow') 

build.gradle 的变化如下:

  • Excludes spring-boot-starter-reactor-netty from the reactive web dependency
  • Introduces spring-boot-starter-undertow as an alternative container

如果我们重新启动我们的应用程序并再次查看自动配置报告 查找ReactorNettyAutoConfiguration< /code> 条目,我们会发现:

    "ReactiveWebServerConfiguration.ReactorNettyAutoConfiguration": { 
      "notMatched": [ 
      { 
        "condition": "OnClassCondition", 
        "message": "@ConditionalOnClass did not find required class 
        'reactor.ipc.netty.http.server.HttpServer'" 
      } 
      ], 
      "matched": [ 
 
      ] 
    } 

来自自动配置报告的新 JSON 片段显示我们刚才查看的相同策略现在已切换到 notMatched。在细节上,它失败了,因为 @ConditionalOnClass 没有在类路径上发现 HttpServer

鉴于从 Reactor Netty 切换到 Undertow,在自动配置报告中搜索 Undertow 将导致我们这样做:

    "ReactiveWebServerConfiguration.UndertowAutoConfiguration": [ 
    { 
      "condition": "OnClassCondition", 
      "message": "@ConditionalOnClass found required class 
      'io.undertow.Undertow'; @ConditionalOnMissingClass did not
       find unwanted class" 
    }, 
    { 
      "condition": "OnBeanCondition", 
      "message": "@ConditionalOnMissingBean (types: 
       org.springframework.boot.web.reactive.server
       .ReactiveWebServerFactory; SearchStrategy: all) did not 
       find any beans" 
    } 
    ] 

 

 

 

 

 

 

 

 

这个 JSON 片段显示 UndertowAutoConfiguration 现在生效,如下所示:

  • @ConditionalOnClass has found Undertow on the classpath
  • @ConditionalOnMissingBean has not found a user-defined ReactiveWebServerFactory bean; hence, Spring Boot did not back off with its autoconfiguration of Undertow.

在进一步深入研究 UndertowReactiveWebServerFactory 时,我们将找到为基于 Reactor 的应用程序运行 Undertow 所需的所有细节。

Making local changes and seeing them on the target system


到目前为止,我们已经了解了如何通过使用自动重启来加快开发人员的时间,并且我们已经收集了有关 Spring Boot 的信息,这要归功于它的自动配置报告。

开发人员的下一步通常是使用 IDE 的调试器。我们不会对此进行详细介绍 因为它高度特定于您使用的 IDE。但是,Spring Boot 提供的扩展价值是远程连接到 应用程序 并进行更改的机会。

想象一下,我们已经构建了我们的 应用程序 并将其推送到云端。我们在此环境中测试一个关键功能,因为这是将其与特定资源或特定配置相关联的唯一方法。好吧,进行更改的过程要昂贵得多。我们将不得不捆绑、重新部署、重新启动和重新导航。只需几行代码!

Spring Boot 的 DevTools 提供了将我们的 IDE 连接到远程运行的应用程序并通过网络推送代码更改的方法,从而使我们能够自动制作模块并立即对其进行测试。

为了做好准备,我们必须执行以下步骤:

  1. Add spring.devtools.remote.secret=learning-spring-boot to application.properties.
  2. Build the application using ./gradlew build.

 

  1. Push the application to the cloud (Pivotal Web Services in this case with cf push learning-spring-boot -p build/libs/learning-spring-boot-0.0.1-SNAPSHOT.jar).
  2. Instead of running the app locally in our IDE, run Spring Boot's RemoteSpringApplication class instead.
  3. Add https://learning-spring-boot.cfapps.io (or whatever the app's remote URL is) as a program argument.
  1. Launch the RemoteSpringApplication configured runner.

以下屏幕截图显示了如何在 IntelliJ IDEA 中配置

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

启动后,我们 IDE 中的控制台会显示 remote 横幅:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

 

现在,我们可以自由地在我们的 IDE 中进行更改,保存/制作项目,并观察它们传播到我们在 https://learning-spring-boot 上运行的基于云的应用程序。 cfapps.io

首先,让我们在 src/main/resources/templates/index.html 处调整我们的模板。我们可以在主标题下方添加一个子标题,类似于我们在本章前面所做的:

    <h1>Learning Spring Boot - 2nd Edition</h1> 
 
    <h2>It's really handy to make local edits and watch them go out
     to the cloud automatically</h2> 
 
    <h4 th:text="${extra}"></h4> 

点击 Save 或 Make Project,代码更改将上传到云端并触发重启(这是使用 LiveReload 服务器并自动刷新页面的好机会):

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

通过这个流程,我们可以进行 各种更改。准备就绪后,我们可以在本地commit 它们,构建一个更新的 JAR 文件,推送到云端,然后继续前进。

Note

总是建议在连接到远程应用程序时使用 https://。它可以防止其他用户窥探网络的秘密。

Note

在远程应用程序上启用 Spring Boot DevTools 是有风险的。保护应用程序免受代码更新的唯一方法是两者共享的简单秘密。您永远不应在生产部署中启用此功能。

 

 

 

 

 

Writing a custom health check


当我们将应用程序投入生产时,需要的另一个关键特性是监控它。在过去,人们会设置一个 CRON 作业来 ping 服务器并查看它是否已启动。更复杂的系统会跟踪磁盘使用情况、内存使用情况,并在数据库达到 95% 时对某人进行寻呼,因此可以在崩溃之前保存它。

Spring Boot 提供了health 检查监控的新纪元。首先,启动应用程序并访问 /application/health

{    status: "UP",    diskSpace: {        status: "UP",        total: 498937626624,        free: 96519303168,        threshold: 10485760    },    mongo: {        status: "UP",        version: "3.4.6"    }}

开箱即用,这为我们提供了一个可以 ping 通的端点,此外,还为我们提供了一些有关磁盘空间的信息。它还包括一个自动包含的 MongoDB 健康检查。

但是如果我们需要编写自己的健康检查呢?也许,有一个我们依赖的系统。了解此上游服务是否不可用可能很有价值。

要编写我们自己的健康检查,我们只需要编写一个实现 Spring Boot 的 HealthIndicator 接口的 Spring 组件:

    @Component 
    public class LearningSpringBootHealthIndicator 
     implements HealthIndicator { 
 
       @Override 
       public Health health() { 
         try { 
           URL url = 
             new URL("http://greglturnquist.com/books/learning-spring-
              boot"); 
             HttpURLConnection conn = 
               (HttpURLConnection) url.openConnection(); 
             int statusCode = conn.getResponseCode(); 
             if (statusCode >= 200 && statusCode < 300) { 
               return Health.up().build(); 
             } else { 
                 return Health.down() 
                  .withDetail("HTTP Status Code", statusCode) 
                  .build(); 
             } 
         } catch (IOException e) { 
             return Health.down(e).build(); 
         } 
       } 
    } 

让我们剖析一下这个自定义健康指标:

  • @Component marks this class so that Spring Boot picks it up and registers it automatically.
  • By implementing the HealthIndicator interface, Spring Boot will include it along with the pre-built health checks when we hit /application/health.
  • The name LearningSpringBootHealthIndicator is used to create the indicator. HealthIndicator will be trimmed off, and the remaining text will be formatted with lazy camel style.
  • There is but one method in this interface (meaning you could implement it using a Java 8 lambda), health(). It uses some plain old Java APIs to open a connection to a remote URL and fetch a status code. If the status code is good, it will build a Health status code of UP. Otherwise, it will build a Health status code of DOWN while also giving us the failed HTTP status code.
  • Finally, if any other exceptions occur, we will also get a Health status code of DOWN but with the information from the exception instead of a commonly coded error path.

让我们重新启动应用程序,看看我们的 /application/health 端点报告了什么:

{  "status": "UP",  "details": {    "mongo": {      "status": "UP",      "details": {        "version": "3.4.6"      }    },    "diskSpace": {      "status": "UP",      "details": {        "total": 498937626624,        "free": 43632435200,        "threshold": 10485760      }    },    "learningSpringBoot": {      "status": "UP"    }  }}

我们可以看到我们的新健康指标 learningSpringBoot,其状态为 UP

为了模拟失败,让我们通过将代码中的域切换到 greglturnquist.io 来更改 URL,然后看看会发生什么:

    URL url = new URL("http://greglturnquist.io/books/learning-spring-
     boot"); 

当我们重新启动并 ping /application/health 时,结果如下:

{  "status": "DOWN",  "details": {    "mongo": {      "status": "UP",      "details": {        "version": "3.4.6"      }    },    "diskSpace": {      "status": "UP",      "details": {        "total": 498937626624,        "free": 43629961216,        "threshold": 10485760      }    },    "learningSpringBoot": {      "status": "DOWN",      "details": {        "error": "java.net.UnknownHostException: greglturnquist.io"      }    }  }}

发生了几件事:

  • Our learningSpringBoot indicator now reports DOWN. It's not due to some HTTP status code, but instead ConnectionException caused by not being able to form a connection.
  • While diskSpace and mongo are UP, the DOWN status of this indicator percolates to the top-level status, switching it to DOWN.

如果我们将 URL 更改为简单的 http://greglturnquist.com/foo 并重新启动,我们可以看到不同的状态:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

在这种情况下,我们仍然有 DOWN 状态,但是报告了 HTTP 状态码 404这些 指标对于观察我们应用程序的DevOps 团队来说非常有用。

Adding build data to /application/info


了解问题核心的最大问题之一是知道正在运行的版本!您是否曾经接到客户报告系统损坏的凌晨 3:00 电话?在半醒的状态下,很容易开始尝试解决问题,但两个小时后才发现,客户正在running 一个较旧的版本,并且他们的问题已在上周修复。

解决方案是在每个版本中嵌入精确的版本,以便客户可以通过电话转发。然后,我们可以快速确定此问题是新问题、已修复问题还是回归问题。感兴趣的?

只需将其添加到 build.gradle 文件中,就在 buildscripts 部分的正下方:

    id "com.gorylenko.gradle-git-properties" version "1.4.17" 

这将向我们的系统添加一个新任务 generateGitProperties。每当我们使用 Gradle 构建应用程序时,无论是打包一个可运行的 JAR 还是简单地启动它,都会生成一个新的 build/resources/main/git.properties 文件并通过 Spring Boot Actuator 的 /application/info 端点提供服务:

{    git: {        commit: {            time: 1474434957000,            id: "3ac9c1c"        },        branch: "master"    }}

该报告为我们提供了时间戳、git commit hash 和分支。从长远来看,这一小块知识有可能为我们节省小时的工作量。

使用 Maven?有一个类似的插件:

    <build> 
        <plugins> 
            <plugin> 
                <groupId>pl.project13.maven</groupId> 
                <artifactId>git-commit-id-plugin</artifactId> 
            </plugin> 
        </plugins> 
    </build> 

它的工作原理相同。

一个额外的花絮——Spring Boot 有两种不同的 git 信息模式。显示的格式是 SIMPLE 模式。要获取更多详细信息,请将其添加到 application.properties

    management.info.git.mode=full 

这将产生更详细的报告:

{    git: {        commit: {            message: {                full: "Move images back to 1/image",                short: "Move images back to 1/image"            },            time: 1474434957000,            id: "3ac9c1c7875d7378d6fbd607d0af5ef206e21ede",            id.abbrev: "3ac9c1c",            user: {                email: "[email protected]",                name: "Greg Turnquist"            }        },        branch: "master"    }}

由每个团队决定哪个版本最有用,哪个版本不会泄露不必要的细节。

此外,我们可以通过将其添加到我们的 build.gradle 来获取有关 build 的更多详细信息 文件:

    springBoot { 
      buildInfo() 
    } 

这个小小的添加,当我们运行 Gradle 的 build 任务时,会在我们的 JAR 文件中添加一个 build-info.properties 文件,显示像这样的内容:

#Properties#Tue Sep 12 23:53:05 CDT 2017build.time=2017-09-12T23:53:05-0500build.artifact=5/part2build.group=learning-spring-boot
build.name=5/part2build.version=unspecified

这两个报告(一个简单的 git 报告 + 构建信息详细信息)都会为我们提供一些有用的信息,通过访问 localhost:8080/application/info 开始调试问题.

Creating custom metrics


每个项目经理喜欢 指标。事实上,一家受欢迎的公司 (Netflix) 在这个领域非常有名,人们将其描述为一家碰巧流式传输视频的指标收集公司。

对于 Spring Boot,指标是 Spring Boot Actuator 功能的主要部分。如果我们访问/application/metrics,我们可以看到一个列表指标:

    {
      "names": [
        "jvm.buffer.memory.used",
        "jvm.memory.used",
        "jvm.buffer.count",
        "logback.events",
        "process.uptime",
        "jvm.memory.committed",
        "http.server.requests",
        "jvm.buffer.total.capacity",
        "jvm.memory.max",
        "process.starttime"
      ]
    }

这列出了所有种类的东西——内存、垃圾收集、堆与非堆、线程等等。这很好,但通常需要的是创建我们自己的指标的能力。

Spring Boot 提供了一个接口来注册我们自己的指标并让它们出现在同一页面上。立即提供的是获取 MeterRegistry 的能力。

为了使用这个三米注册表,我们需要将它注入到我们在 ImageService ">第 12 章使用 Spring Boot 进行响应式数据访问

    @Service 
    public class ImageService { 
 
      ... 
 
      private final MeterRegistry meterRegistry; 
 
      public ImageService(ResourceLoader resourceLoader, 
        ImageRepository imageRepository, 
        MeterRegistry meterRegistry) { 
 
          this.resourceLoader = resourceLoader; 
          this.imageRepository = imageRepository; 
          this.meterRegistry = meterRegistry; 
        } 
        ... 

此代码显示以下内容:

  • Three metric services, CounterService, GaugeService, and InMemoryMetricRepository declared as final attributes
  • These three fields are populated by constructor injection, ensuring they are supplied when the service is created

有了它,在 createImage 内部,我们可以定义自定义指标:

    public Mono<Void> createImage(Flux<FilePart> files) { 
      return files 
       .log("createImage-files") 
       .flatMap(file -> { 
         Mono<Image> saveDatabaseImage = imageRepository.save( 
           new Image( 
             UUID.randomUUID().toString(), 
             file.filename())) 
             .log("createImage-save"); 
 
             Mono<Void> copyFile = Mono.just(Paths.get(UPLOAD_ROOT,
             file.filename()).toFile()) 
             .log("createImage-picktarget") 
             .map(destFile -> { 
               try { 
                 destFile.createNewFile(); 
                 return destFile; 
               } catch (IOException e) { 
                   throw new RuntimeException(e); 
               } 
             }) 
             .log("createImage-newfile") 
             .flatMap(file::transferTo) 
             .log("createImage-copy"); 
 
             Mono<Void> countFile = Mono.fromRunnable(() -> { 
               meterRegistry
                  .summary("files.uploaded.bytes")
                  .record(Paths.get(UPLOAD_ROOT,
                   file.filename()).toFile().length())
             }); 
 
             return Mono.when(saveDatabaseImage, copyFile, countFile) 
              .log("createImage-when"); 
       }) 
       .log("createImage-flatMap") 
       .then() 
       .log("createImage-done"); 
    } 

创建新图像的代码的第一部分是相同的,但接下来是 meterRegistry.summary("files.uploaded.bytes").record(...),这会创建一个名为 files.uploaded.bytes 的新 distribution summary。分发摘要包括名称、可选标签和值。注册的内容既是一个值,也是一个事件。每次添加一个计量器时,都会对其进行计数,并将 运行总数制成表格。

通过这些调整,我们可以刷新应用程序,等待它重新加载,然后上传几张图片,如下所示:

读书笔记《developing-java-applications-with-spring-and-spring-boot-ebook》用于Spring Boot Apps的开发工具

上传这些图片后,如果我们重新访问 /application/metrics,我们可以看到我们新的 metric 在列表底部:

    {
      "names": [
        "jvm.buffer.memory.used",
        "jvm.memory.used",
        "jvm.buffer.count",
        "logback.events",
        "process.uptime",
        "jvm.memory.committed",
        "http.server.requests",
        "jvm.buffer.total.capacity",
        "jvm.memory.max",
        "process.starttime",
        "files.uploaded.bytes"
      ]
    }

如果我们导航到http://localhost:8080/application/metrics/files.uploaded.bytes,我们可以查看:

    {
      "name": "files.uploaded.bytes",
      "measurements": [
        {
          "statistic": "Count",
          "value": 3.0
        },
        {
          "statistic": "Total",
          "value": 208020.0
        }
      ],
      "availableTags": [

      ]
    }

此 JSON 显示 三个 测量已注册到files.uploaded.bytes,总计208020字节。没有立即显示的还有这些指标的发布时间。可以使用新的 Micrometer 模块(http://micrometer.io)。

Note

Micrometer 是 Pivotal 的一个新项目。它是度量收集的门面。想想 SLF4J,但要考虑指标。它旨在与许多度量收集系统集成,包括 Atlas、Prometheus、Datadog、Influx、Graphite 等。在这种情况下,它使用的是基于内存的解决方案。由于它目前正在开发中并且可以保证它的自己的书,我们不会深入研究。

这只是可以定义的可能指标的一个示例。随意挖掘和试验数据。

 

 

 

 

 

 

Working with additional Actuator endpoints


Spring Boot Actuator 提供 很多 额外数据。以下 table 是一个快速总结:

执行器端点

说明

审计事件

公开当前应用程序的审核 events

自动配置

报告 Spring Boot 做了什么 没有自动配置以及为什么

豆子

报告所有配置 in 应用程序上下文的 bean(包括我们的以及 Boot 自动配置的)

配置道具

公开 all 配置属性

环境

当前系统环境的报告

健康

一个简单的端点检查 应用程序的生命周期

堆转储

返回一个 GZip 压缩的 hprofdump 文件(hprof 是每个 JDK 的工具)

信息

从应用程序提供自定义 content

日志文件

返回 日志文件的内容(假设 logging.filelogging.path 已设置)

记录器

列出所有配置的记录器它们的级别。还支持通过 POST 操作更新日志级别。

指标

显示计数器衡量网络使用情况

映射

提供我们关于all< /a> Spring WebFlux 路由

地位

线程转储

创建线程转储报告

痕迹

Note

每一个都以 /application/ 为前缀(默认情况下)。例如,health 位于 /application/health。要覆盖此前缀,只需将 management.context-path 添加到 application.properties 并换出您喜欢的前缀(例如  /manager)。此外,management.context-path 相对于 server.context-path

可以调整提供 Actuator 端点的端口。将 management.port 属性设置为 8081 会将所有这些端点的端口更改为 8081 。我们甚至可以通过设置management.address=127.0.0.1来调整使用的网络地址。此设置将使这些信息丰富的端点仅对本地框可见,并减少对外部连接的可见性。

Summary


在本章中,我们连接了 Spring Boot 的 DevTools 模块。这使得使用嵌入式 LiveReload 服务器以及取消缓存模板成为可能。我们使用 Spring Boot 的自动配置报告来收集有关嵌入式容器的信息。然后,我们用 Undertow 替换了 Netty,并通过同一份报告对其进行了验证。我们涉足编写自定义健康检查和自定义指标。然后,我们通过将构建信息嵌入应用程序来解决问题,以便在我们从 Ops 中心接到深夜电话时发现操作中的版本。

在下一章中,我们将学习如何使用容错 高级消息队列协议 (AMQP) 消息在进程之间进行通信。