|
| 1 | +在之前的系列文章中我们学习了如何进行Spring Boot应用的功能开发,以及如何写单元测试、集成测试等,然而,在实际的软件开发中需要做的不仅如此:还包括对应用程序的监控和管理。 |
| 2 | + |
| 3 | +正如飞行员不喜欢盲目飞行,程序员也需要实时看到自己的应用目前的运行情况。如果给定一个具体的时间,我们希望知道此时CPU的利用率、内存的利用率、数据库连接是否正常以及在给定时间段内有多少客户请求等指标;不仅如此,我们希望通过图表、控制面板来展示上述信息。最重要的是:老板和业务人员希望看到的是图表,这些比较直观易懂。 |
| 4 | + |
| 5 | +首先,这篇文章讲介绍如何定制自己的health indicator。 |
| 6 | + |
| 7 | +## How Do |
| 8 | +- 在pom文件中添加*spring-boot-starter-actuator*依赖 |
| 9 | +``` |
| 10 | +<dependency> |
| 11 | + <groupId>org.springframework.boot</groupId> |
| 12 | + <artifactId>spring-boot-starter-actuator</artifactId> |
| 13 | +</dependency> |
| 14 | +``` |
| 15 | +- *spring-boot-starter-actuator*这个库让我们可以访问应用的很多信息,包括:/env、/info、/metrics、/health等。现在运行程序,然后在浏览器中访问:http://localhost:8080/health,将可以看到下列内容。 |
| 16 | + |
| 17 | +- 除了/health可以访问,其他的Endpoints也可以访问,例如/info:首先在*application.properties*文件中添加对应的属性值,符号*@*包围的属性值来自pom.xml文件中的元素节点。 |
| 18 | +``` |
| 19 | +info.build.artifact=@project.artifactId@ |
| 20 | +info.build.name=@project.name@ |
| 21 | +info.build.description=@project.description@ |
| 22 | +info.build.version=@project.version@ |
| 23 | +``` |
| 24 | +- 要获取配置文件中的节点值,需要在pom文件中进行一定的配置,首先在<build>节点里面添加: |
| 25 | +``` |
| 26 | +<resources> |
| 27 | + <resource> |
| 28 | + <directory>src/main/resources</directory> |
| 29 | + <filtering>true</filtering> |
| 30 | + </resource> |
| 31 | +</resources> |
| 32 | +``` |
| 33 | +然后在<plugins>节点里面增加对应的插件: |
| 34 | +``` |
| 35 | +<plugin> |
| 36 | + <groupId>org.apache.maven.plugins</groupId> |
| 37 | + <artifactId>maven-resources-plugin</artifactId> |
| 38 | + <version>2.6</version> |
| 39 | + <configuration> |
| 40 | + <delimiters> |
| 41 | + <delimiter>@</delimiter> |
| 42 | + </delimiters> |
| 43 | + <useDefaultDelimiters>false</useDefaultDelimiters> |
| 44 | + </configuration> |
| 45 | +</plugin> |
| 46 | +``` |
| 47 | +- 然后运行应用程序,访问http://localhost:8080/info,可以看到下列信息 |
| 48 | + |
| 49 | +- 除了使用系统默认的监控信息,我们还可以定义自己的health indicator。使用[Spring Boot:定制自己的starter](http://www.jianshu.com/p/85460c1d835a)一文中做过的db-count-starter作为观察对象,我们希望监控每个数据库接口的运行状况:如果某个接口返回的个数大于等于0,则表示系统正常,表示为UP状态;否则,可能该接口发生异常,表示为DOWN状态。首先,将DbCountRunner类中的getRepositoryName方法由private转为protected,然后在db-count-starter这个模块中也添加actuator依赖。 |
| 50 | +- 在*db-count-starter/src/main/com/test/bookpubstarter*目录下创建*DbCountHealthIndicator.java*文件 |
| 51 | +``` |
| 52 | +public class DbCountHealthIndicator implements HealthIndicator { |
| 53 | + private CrudRepository crudRepository; |
| 54 | + public DbCountHealthIndicator(CrudRepository crudRepository) { |
| 55 | + this.crudRepository = crudRepository; |
| 56 | + } |
| 57 | + @Override |
| 58 | + public Health health() { |
| 59 | + try { |
| 60 | + long count = crudRepository.count(); |
| 61 | + if (count >= 0) { |
| 62 | + return Health.up().withDetail("count", count).build(); |
| 63 | + } else { |
| 64 | + return Health.unknown().withDetail("count", count).build(); |
| 65 | + } |
| 66 | + } catch (Exception e) { |
| 67 | + return Health.down(e).build(); |
| 68 | + } |
| 69 | + } |
| 70 | +} |
| 71 | +``` |
| 72 | +- 最后,还需要注册刚刚创建的健康监控器,在DbCountAutoConfiguration.java中增加如下定义: |
| 73 | +``` |
| 74 | +@Autowired |
| 75 | +private HealthAggregator healthAggregator; |
| 76 | +@Bean |
| 77 | +public HealthIndicator dbCountHealthIndicator(Collection<CrudRepository> repositories) { |
| 78 | + CompositeHealthIndicator compositeHealthIndicator = new |
| 79 | + CompositeHealthIndicator(healthAggregator); |
| 80 | + for (CrudRepository repository: repositories) { |
| 81 | + String name = DbCountRunner.getRepositoryName(repository.getClass()); |
| 82 | + compositeHealthIndicator.addHealthIndicator(name, new DbCountHealthIndicator(repository)); |
| 83 | + } |
| 84 | + return compositeHealthIndicator; |
| 85 | +} |
| 86 | +``` |
| 87 | +- 运行程序,然后访问http://localhost:8080/health,则可以看到如下结果 |
| 88 | + |
| 89 | + |
| 90 | + |
| 91 | +## 分析 |
| 92 | +Spring Boot Autuator这个库包括很多自动配置,对外开放了很多endpoints,通过这些endpoints可以访问应用的运行时状态: |
| 93 | +- */env*提供应用程序的环境变量,如果你在调试时想知道某个配置项在运行时的值,可以通过这个endpoint访问——访问*http://localhost:8080/env*,可以看到很多方面的配置,例如,class path resources—[tomcat.https.properties]、applicationConfig—[classpath:/application.properties]、commonsConfig、systemEnvironment、systemProperties等。 |
| 94 | +这些变量的值由*Environment*实例中的*PropertySource*实例保存,根据这些属性值所在的层次,有可能在运行时已经做了值替换,跟配置文件中的不一样了。为了确认某个属性的具体值,例如*book.count.rate*属性,可以访问*http://localhost:8080/env/book.counter.rate*来查询,如果跟配置文件中的不一样,则可能是被系统变量或者命令行参数覆盖了。*EnvironmentEndpoint*类负责实现上述功能,有兴趣可以再看看它的源码; |
| 95 | +- */configprops*提供不同配置对象,例如*WebConfiguration.TomcatSslConnectionProperties*,它与/env不同的地方在于它会表示出与配置项绑定的对象。尝试下访问*http://localhost:8080/configprops*,然后在网页中查询*custom.tomcat.https*,可以看到我们之前用于配置*TomcatSslConnector*对象的属性值(参见:[让你的Spring Boot工程支持HTTP和HTTPS](http://www.jianshu.com/p/c3879add87ec))。 |
| 96 | + |
| 97 | +- */autoconfig*以web形式对外暴露AutoConfiguration 信息,这些信息的解释可以参考[Spring Boot:定制自己的starter](http://www.jianshu.com/p/85460c1d835a)一文,这样我们就不需要通过“修改应用程序的日志级别和查看应用的启动信息”来查看应用的自动配置情况了。 |
| 98 | +- */beans*,这个endpoint列出所有由Spring Boot创建的bean。 |
| 99 | + |
| 100 | +- */mapping*,这个endpoint显示当前应用支持的URL映射,该映射关系由*HandlerMapping*类维护,通过这个endpoint可以查询某个URL的路由信息。 |
| 101 | + |
| 102 | +- */info*,这个endpoint显示应用程序的基本描述,在之前的实践例子中我们看过它的返回信息,属性值来自*appliaction.properties*,同时也可以使用占位符获取pom.xml文件中的信息。任何以*info.*开头的属性都会在访问*http://localhost:8080/info*时显示。 |
| 103 | +- */health*提供应用程序的健康状态,或者是某个核心模块的健康状态。 |
| 104 | +- */metrics*,这个endpoint显示Metrics 子系统管理的信息,后面的文章会详细介绍它。 |
| 105 | + |
| 106 | +上述各个endpoint是Spring Boot Actuator提供的接口和方法,接下来看看我们自己定制的*HealthIndicator*,我们只需要实现*HealthIndicator*接口,Spring Boot会收集该接口的实现,并加入到*/health*这个endpoint中。 |
| 107 | + |
| 108 | +在我们的例子中,我们为每个*CrudRepository*实例都创建了一个*HealthIndicator*实例,为此我们创建了一个*CompositeHealthIndicator*实例,由这个实例管理所有的*DbHealthIndicator*实例。作为一个*composite*,它会提供一个内部的层次关系,从而可以返回JSON格式的数据。 |
| 109 | + |
| 110 | +代码中的*HealthAggregator*实例的作用是:它维护一个map,告诉*CompositeHealthIndicator*如何决定所有*HealthIndicator*代表的整体的状态。例如,除了一个repository返回DOWN其他的都返回UP,这时候这个composite indicator作为一个整体应该返回UP还是DOWN,*HealthAggregator*实例的作用就在这里。 |
| 111 | + |
| 112 | +Spring Boot使用的默认的*HealthAggregator*实现是*OrderedHealthAggregator*,它的策略是手机所有的内部状态,然后选出在DOWN、OUT_OF_SERVICE、UP和UNKNOWN中间具有最低优先级的那个状态。这里使用策略设计模式,因此具体的状态判定策略可以改变和定制,例如我们可以创建定制的*HealthAggregator*: |
| 113 | + |
| 114 | +最后需要考虑下安全问题,通过这些endpoints暴露出很多应用的信息,当然,Spring Boot也提供了配置项,可以关闭指定的endpoint——在application.properties中配置*<name>.enable=false*; |
| 115 | + |
| 116 | +还可以通过设置*management.port=-1*关闭endpoint的HTTP访问接口,或者是设置其他的端口,供内部的admin服务访问;除了控制端口,还可以设置仅仅让本地访问,只需要设置*management.address=127.0.0.1*;通过设置*management.context-path=/admin*,可以设置指定的根路径。综合下,经过上述设置,在本地访问*http://127.0.0.1/admin/health*来访问健康状态。 |
| 117 | + |
| 118 | +可以在防火墙上屏蔽掉不是/admin/*的endpoints访问请求,更进一步,利用Spring Security可以配置验证信息,这样要访问当前应用的endpoints必须使用用户名和密码登陆。 |
| 119 | + |
| 120 | +## 参考资料 |
| 121 | +1. [Endpoints](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html) |
| 122 | +2. [Complete Guide for Spring Boot Actuator](http://www.javabeat.net/spring-boot-actuator/) |
0 commit comments