spring-boot-admin为我们基于spring-boot的基础数据安全端口提供了基础的可视化监控功能。还可以通过spring-boot-admin的server程序对spring-boot程序提供简单的实时管理(例如修改日志输出级别)。
通过官方的英文指导文档,可以很方便的学习spring-boot-admin的集成。根据个人的学习过程简单整理了一下,希望对想要学习的童鞋有所帮助。
注意,搭建demo的过程中采用了maven来构建,在父pom中统一配置spring-boot、spring-cloud以及spring-boot-admin相关组件的版本,所以在子模块中,引入依赖时不会显示指明版本。
以下是父pom中的的主要配置
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<admin.version>1.5.4</admin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server</artifactId>
<version>${admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui</artifactId>
<version>${admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui-hystrix</artifactId>
<version>${admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui-turbine</artifactId>
<version>${admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui-login</artifactId>
<version>${admin.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
一、spring-boot + spring-boot-admin 集成基础
1.1、利用spring-boot-starter-actuator创建安全端点
添加maven依赖
首先,我们创建一个admin-client作为spring-boot-admin的客户端程序。该程序依赖如下两个依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.properties文件配置
我们设置内嵌tomcat监听端口为8090
spring.application.name=admin-client
server.port=8090
#关闭安全控制
management.security.enabled=false
启动类配置
@SpringBootApplication
public class AdminClient {
public static void main(String[] args) {
SpringApplication.run(AdminClient.class, args);
}
}
测试效果
直接启动main方法,可以看到日志中有如下几行记录,里面有spring-boot-starter-actuator为我们暴露的信息
2017-10-22 16:59:22.572 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/loggers/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint.get(java.lang.String)
2017-10-22 16:59:22.573 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/loggers/{name:.*}],methods=[POST],consumes=[application/vnd.spring-boot.actuator.v1+json || application/json],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint.set(java.lang.String,java.util.Map<java.lang.String, java.lang.String>)
2017-10-22 16:59:22.573 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/loggers || /loggers.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-10-22 16:59:22.579 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/health || /health.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(javax.servlet.http.HttpServletRequest,java.security.Principal)
2017-10-22 16:59:22.581 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/mappings || /mappings.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-10-22 16:59:22.583 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/info || /info.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-10-22 16:59:22.584 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/autoconfig || /autoconfig.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-10-22 16:59:22.586 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/heapdump || /heapdump.json],methods=[GET],produces=[application/octet-stream]}" onto public void org.springframework.boot.actuate.endpoint.mvc.HeapdumpMvcEndpoint.invoke(boolean,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.io.IOException,javax.servlet.ServletException
2017-10-22 16:59:22.587 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/configprops || /configprops.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-10-22 16:59:22.591 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/beans || /beans.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-10-22 16:59:22.598 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/trace || /trace.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-10-22 16:59:22.600 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/dump || /dump.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-10-22 16:59:22.601 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/metrics/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String)
2017-10-22 16:59:22.602 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/metrics || /metrics.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
2017-10-22 16:59:22.605 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/auditevents || /auditevents.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public org.springframework.http.ResponseEntity<?> org.springframework.boot.actuate.endpoint.mvc.AuditEventsMvcEndpoint.findByPrincipalAndAfterAndType(java.lang.String,java.util.Date,java.lang.String)
2017-10-22 16:59:22.606 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/env/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String)
2017-10-22 16:59:22.607 INFO 341860 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/env || /env.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke()
我们简单访问health端点地址:http://127.0.0.1:8090/health,可以得到如下程序健康信息结果
{"status":"UP","diskSpace":{"status":"UP","total":104857595904,"free":55882469376,"threshold":10485760}}
由于端点众多且与其他框架例如spring-cloud集成时还会增加其他端点,非常不方便记忆核操作。同时直接访问的时候返回的数据都是JSON格式的字符串,查看不太直观。所以这就有了spring-boot-admin,它可以为我们提供这些端点比较直观的UI界面效果,方便我们的运营、运维、技术等人员使用。
1.2、创建spring-boot-admin服务端程序admin-server
我们创建admin-server作为spring-boot-admin的server端程序,为该程序添加jar包依赖,该依赖包括自动装配依赖和UI依赖
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui</artifactId>
</dependency>
配置监听端口核程序名等
spring.application.name=admin-server
server.port=80
创建核配置启动类
@SpringBootApplication
@EnableAdminServer
public class AdminServer {
public static void main(String[] args) {
SpringApplication.run(AdminServer.class,args);
}
}
@EnableAdminServer注解表示开启spring-boot-admin-server程序。直接启动main方法后,我们访问http://127.0.0.1,可以看到简单的界面效果,由于目前没有客户端程序所以列表中没有记录
接下来我们将之前的admin-client程序集成spring-boot-admin-starter-client,先修改maven依赖
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
其次,我们在配置文件中新增如下配置
#设置admin-server的地址
spring.boot.admin.url=http://127.0.0.1
然后我们启动admin-client程序,我们可以看看admin-client程序已经可以在admin-server程序的UI界面中看到了
点击Details按钮,可以看到更多程序的信息,例如
从图中我们可以看到内存使用的情况,这些数据都是通过admin-client程序的端点获取到的信息并展示给我们。信息非常多,更多细节功能可以自己探索,这里不做赘述。
小结
从以上简单的例子中,我们可以发现一般的spring-boot程序使用spring-boot-admin作为管理工具需要经过如下两个步骤:
- 1、需要创建一个spring-boot-admin的server程序。
- 2、为应用程序集成spring-boot-admin-stater-client客户端。
- 3、客户端配置关闭掉端口的安全控制以及配置上admin-server的URL地址。
- 4、spring-boot-admin的管理程序必须与客户端程序必须网络可达。
1.3 为admin-server程序添加安全访问控制
admin-server程序是spring-boot的安全端点管理程序,自身的安全也是比不可少的,所以我们需要为admin-server程序添加安全访问控制。官方已经提供了对应的支持,具体步骤如下:
- 添加maven依赖
admin-server的登录安全采用的是spring-security来实现的,官方为我们提供了UI界面。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui-login</artifactId>
</dependency>
- 配置用户名核密码
在配置文件application.properties中添加登录访问密码
security.user.name=user
security.user.password=123456
- 配置WebSecurity安全拦截
最后,我们还需要在admin-server中配置security的拦截配置,同样官方也为我们提供了代码,如下
@Configuration
public static class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// Page with login form is served as /login.html and does a POST on /login
http.formLogin().loginPage("/login.html").loginProcessingUrl("/login").permitAll();
// The UI does a POST on /logout on logout
http.logout().logoutUrl("/logout");
// The ui currently doesn't support csrf
http.csrf().disable();
// Requests for the login page and the static assets are allowed
http.authorizeRequests()
.antMatchers("/login.html", "//*.css", "/img/", "/third-party/")
.permitAll();
// ... and any other request needs to be authorized
http.authorizeRequests().antMatchers("/").authenticated();
// Enable so that the clients can authenticate via HTTP basic for registering
http.httpBasic();
}
}
重启程序,再次访问时,就可以看到访问admin-server需要输入用户名和密码了
二、spring-cloud + spring-boot-admin集成
微服务盛行的今天,基于spring-boot的微服务框架spring-cloud进入大众视野,许多大公司也纷纷开始使用该框架,都在尝试适用于自己平台的组件。大家都知道spring-cloud框架程序间是通过微服务的注册发现来实现相互的感知的。
spring-boot-admin官方也为我们提供了基于spring-cloud框架的支持。下面我们逐步来实现spring-boot-admin在spring-cloud下的集成使用。
我们需要创建如下三个程序
程序名 | 功能说明 |
---|---|
eureka-server | spring-cloud的eureka实现的注册中心。 |
admin-cloud-server | spring-boot-admin的服务端程序,类似与前面的admin-server。 |
admin-cloud-server | spring-boot-admin的客户端程序,类似与前面的admin-client。 |
2.1 基于spring-cloud的基础搭建
2.1.1eureka-server创建和配置
我们使用spring-cloud提供的eureka组件搭建一个简单的注册中心程序。搭建非常简单。
- 创建eureka-server程序并添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
- application.properties文件配置
spring.application.name=eureka-server
server.port=8761
#配置不进行自我注册核信息拉取
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
#注册中心地址,其他程序通过次URL进行注册
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
eureka.instance.instance-id=${spring.cloud.client.ipAddress}:${server.port}
eureka.instance.prefer-ip-address=true
为了方便我们后边测试,在application.properties中可以添加如下配置来关闭掉注册中心自我保护模式以及剔除客户端的时间周期
#是否打开自我保护,如果设置为true,很容易会出现红色字体的自我保护提示,不方便测试
eureka.server.enable-self-preservation=false
#清理时间,默认是60*1000毫秒,修改短一点,保证开发时,客户端失效后,能够快速剔除客户端
eureka.server.eviction-interval-timer-in-ms=10000
- 配置启动类
我们在启动类上添加注册中心相关注解
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer {
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class,args);
}
}
这样我们的注册中心就配置好了,启动主类即可。
2.1.2、admin-cloud-server创建和配置
与上一节中admin-server程序创建流程类似,我们创建spring-boot-admin的服务端程序admin-cloud-server
- 添加maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui</artifactId>
</dependency>
- application.properties配置文件配置
spring.application.name=admin-cloud-server
server.port=80
#添加eureka注册信息
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
eureka.instance.instance-id=${spring.cloud.client.ipAddress}:${server.port}
eureka.instance.prefer-ip-address=true
management.security.enabled=false
- 启动类AdminCloudServer配置
@SpringBootApplication
//注册到eureka
@EnableDiscoveryClient
//开启AdminServer
@EnableAdminServer
public class AdminCloudServer {
public static void main(String[] args) {
SpringApplication.run(AdminCloudServer.class, args);
}
}
配置完成后启动配置,启动程序,访问http://127.0.0.1/,可以看到与上一节中admin-server相同的界面
2.13、admin-cloud-client创建和配置
我们创建一个名为admin-cloud-client的spring-boot-admin的客户端程序,同时它也是一个spring-cloud程序。
- 添加maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
注意一定要添加spring-boot-starter-actuator
- application.properties配置
spring.application.name=admin-cloud-client
server.port=8090
#注册中心地址
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
eureka.instance.instance-id=${spring.cloud.client.ipAddress}:${server.port}
eureka.instance.prefer-ip-address=true
#关闭安全控制
management.security.enabled=false
- 启动类配置
@SpringBootApplication
@EnableDiscoveryClient
public class AdminCloudClient {
public static void main(String[] args) {
SpringApplication.run(AdminCloudClient.class,args);
}
}
我们启动该程序,稍后刷新admin-cloud-server的管理页面,可以看到如下效果
可以看到,admin-cloud-client程序也出现在了管理列表中
我们查看eureka-server程序的管理页面http://127.0.0.1:8761,可以看到admin-cloud-server和admin-cloud-client都在注册中心列表中
这样,我们基于spring-cloud注册中心方式的spring-boot-admin管理程序就搭建好了。
小结
从原来的单纯的spring-boot版本改为当前的基于spring-cloud注册中心方式的版本非常简单,大致步骤如下:
- 1、首先我们要添加eureka客户端支持,都需要添加spring-cloud-starter-eureka依赖
- 2、配置文件中增加eureka注册中心地址。
- 3、启动来添加eureka客户端注解@EnableDiscoveryClient来注册到注册中心。
可以从上面看到,客户端不在需要任何spring-boot-admin客户端的依赖核配置,由于使用了注册中心,使用起来更加简单了。
2.2 基于spring-cloud的扩展监控集成
大家都知道spring-cloud做微服务的一个很重要的特性就是支持hystrix(断路器),解决系统雪崩还对短路情况进行了统计,并支持通过安全端点实时输出。spring-cloud已经提供了hystrix-bashbroad组件UI来动态显示单个断路器的情况,又提供了turbine组件来实现分布式时断路器的数据聚合。虽然原本都提供了UI界面,但并未进行统一集成,在生产环境中使用起来不太方便。
spring-boot-admin不仅添加了spring-cloud的支持,同时还增加了对spring-cloud扩展端点的UI支持。下面我们逐步改进程序来体验一下效果。
实现思路是这样:我们在admin-cloud-client中添加hystrix(断路器)功能支持,并创建一个定时器来让它访问一个不可达的地址。然后在admin-cloud-client中集成hystrix以及turbine监控的UI。观察是否能够达到效果。
2.2.1、admin-cloud-client改造
首先,我们增加hystrix和feign依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
并在启动类采用@EnableCircuitBreaker开启断路器自动装配
@SpringBootApplication
@EnableDiscoveryClient
//开启断路器装配
@EnableCircuitBreaker
//开启feign client
@EnableFeignClients
public class AdminCloudClient {
public static void main(String[] args) {
SpringApplication.run(AdminCloudClient.class,args);
}
}
创建一个FeignClient,但是service-id我们配置为不存在的服务id(test-service)
@FeignClient("test-service")
public interface TestClient {
@RequestMapping("/test")
void test();
}
接下来,我们创建一个定时任务来调用FeignClient客户端访问test方法,方便稍后看到断路器变化代码如下
@Component
//开启定时任务
@EnableScheduling
public class HystrixJob {
@Autowired
private TestClient testClient;
@Scheduled(cron = "0/20 * * * * ?")
public void doJob(){
try{
testClient.test();
}catch(Exception e){
e.printStackTrace();
}
}
}
2.2.2、admin-cloud-server改造
为admin-cloud-server程序添加hystrix的ui依赖
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui-hystrix</artifactId>
</dependency>
先启动admin-cloud-client再启动admin-cloud-server程序。在控制面板中找到“ADMIN-CLOUD-CLIENT”程序点击右边Details按钮,进入admin-cloud-client程序的管理界面,点击hystrix可以看到断路器相关的统计信息
可以看到hystix的UI面板已经集成到了spring-boot-admin中。
接下来我们集成turbine面版UI,首先我们添加turbine的ui和自动装配相关的依赖
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui-turbine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-turbine</artifactId>
</dependency>
上面第一个依赖为我们添加了turbine聚合UI的入口,第二个依赖给程序添加了turbine相关的端点,这样程序访问当前程序才能通过访问自己访问到turbine端口
接着我们得在admin-cloud-server程序启动类上添加@EnableTurbine注解开启turbine支持,如下
@SpringBootApplication
//注册到eureka
@EnableDiscoveryClient
//开启AdminServer
@EnableAdminServer
#开启turbine
@EnableTurbine
public class AdminCloudServer {
public static void main(String[] args) {
SpringApplication.run(AdminCloudServer.class, args);
}
}
最后,需要做turbine相关的配置
spring.boot.admin.turbine.clusters=default
#设置turbine端点的service-id,由于目前turbine功能已经集成到了admin-cloud-server程序中,所以这里填写的是该程序注册到注册中心的service-Id
spring.boot.admin.turbine.location=admin-cloud-server
turbine.clusterNameExpression=new String("default")
#设置需要监控的serviceId
turbine.app-config=admin-cloud-client
2.2.3、效果测试
接下来我们还是依次启动admin-cloud-client和admin-cloud-server程序。登录后admin管理页面,我们可以看到界面增加了新的Turbine入口,界面如下
点击该入口,可以看到一个聚合hystrix信息的turbine面板
这样spring-boot-admin就集成了spring-cloud中turbine组件的功能了。
注意:spirng-boot-admin服务端程序在访问hystrix相关页面的时候会报一个错误
2017-10-24 15:03:21.435 WARN 493284 --- [p-nio-80-exec-7] o.s.c.n.z.f.post.SendResponseFilter : Error while sending response to client: java.io.IOException: 你的主机中的软件中止了一个已建立的连接。
2017-10-24 15:03:21.446 WARN 493284 --- [p-nio-80-exec-7] o.s.c.n.z.filters.post.SendErrorFilter : Error during filtering
com.netflix.zuul.exception.ZuulException: Filter threw Exception
at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:227) ~[zuul-core-1.3.0.jar:1.3.0]
at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:157) ~[zuul-core-1.3.0.jar:1.3.0]
......
官方也有人提了issue,不过这是zuul的bug,详情见github
2.3、基于MQ的hystrix(断路器)消息收集方式集成
大家都知道hystrix数据的收集是通过spring-boot-admin服务端访问hystrix端点获取的,如果在无法访问http或者我们想要减少http方式的请求时,可以使用MQ来作为消息手机通信方式。官方推荐使用RabbitMQ,所以我们需要先安装RabbitMQ。具体安装方法这里就不讲了。
2.3.1、admin-cloud-client改造
spring-cloud提供了hystri的MQ收集工具包,我们在client程序中增加如下两个依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
由于采用了RabbitMQ,我们还需要在application.properties中增加MQ的连接配置
#RabbitMQ的连接配置
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
这样客户端的改造就完成了
2.3.2、admin-cloud-server改造
首先,我们在server端添加如下maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-turbine-amqp</artifactId>
</dependency>
该依赖会自动引入spring-cloud-starter-stream-rabbit与spring-cloud-starter-turbine-stream。
其次,我们删除掉原来的spring-cloud-starter-turbine依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-turbine</artifactId>
</dependency>
接着我们要改造启动类,使用 @EnableTurbineStream 替换 @EnableTurbine
@SpringBootApplication
//注册到eureka
@EnableDiscoveryClient
//开启AdminServer
@EnableAdminServer
//@EnableTurbine
@EnableTurbineStream
public class AdminCloudServer {
public static void main(String[] args) {
SpringApplication.run(AdminCloudServer.class, args);
}
}
接着,application.properties中删除掉之前的如下配置
turbine.clusterNameExpression=new String("default")
#设置需要监控的serviceId
turbine.app-config=admin-cloud-client
除此之外,我们要配置MQ获取到数据后暴露到哪一个端口,假设我们将数据暴露在8088端口,配置如下
# 这是turbine即暴露监控数据的端口,必须跟server.port不同
turbine.stream.port=8088
最后,我们还需要将之前的spring.boot.admin.turbine.location的值admin-cloud-server改为URL的的方式,指向当前程序的8088端口,如下
spring.boot.admin.turbine.location=http://localhost:${turbine.stream.port}/
2.2.3、效果测试
注意: 如果spring.boot.admin.turbine.location不更改为url,会报如下错误,因为端点端口并未暴露到程序监听的80端口,而是暴露到了turbine.stream.port所配置的8088端口
Unable to connect to Command Metric Stream 'api/turbine/stream?cluster=default'.Error: {"isTrusted":true}
同样,我们依次启动admin-cloud-client和admin-cloud-server,在打开server端的turbine面板,我们又可以看到与之前非MQ模式的效果
三、其他功能集成
接下来我们简单集成核配置两个基础功能:客户端HTTP BASIC认证配置与服务端监控邮件通知
3.1、客户端HTTP BASIC认证配置
大家可以看到我们的客户程序中添加了management.security.enabled并设置其值为false,目的是关闭端点的安全访问。在生产环境中,我们的admin-client或者admin-cloud-client可能添加了安全访问可控制,这个时候访问端点就需要提供用户名和密码了。
spring-boot-admin支持客户端程序使用HTTP BASIC的安全认证,接下来我们改进一下客户端基于spring-boot-starter-security搭建的安全访问控制配置。
我们假设,admin-client或者admin-cloud-client中我们使用spring-boot-starter-security做了HTTP BASIC安全认证,配置文件中的密码配置为
security.user.name=test
security.user.password=123456
client为单纯的spring-boot程序
如果是单纯使用spring-boot构建(非spring-cloud)的程序,那么我们需要在客户端配置文件中增加如下配置
spring.boot.admin.client.metadata.user.name=${security.user.name}
spring.boot.admin.client.metadata.user.password=${security.user.password}
client为spring-cloud程序
如果使用了spring-cloud,因为我们使用的是没有spring-boot-admin-starter-client依赖的方式,而是通过eureka来获取客户端信息的,所以需要按照如下方式配置在元数据中
eureka.instance.metadata-map.user.name=${security.user.name}
eureka.instance.metadata-map.user.password=${security.user.password}
这样spring-boot-admin服务端程序就可以正常访问客户端程序了。
3.2、客户端上下线通知
spring-boot-admin作为监控管理程序,除了提供实时的程序安全数据端口UI监控外,官方还提供了spring-boot-admin客户端程序异常的通知预警功能。官网提供了八种通知方式,详情见官网,大家可以根据自己的需求集成不同的通知方式。接下来我们搭建一下使用邮件通知方式的预警功能。spring-boot-admin集成邮件通知功能与spring-boot集成邮件发送功能类似。接下来我们逐步集成改进spring-boot-admin服务端(我们以admin-cloud-server为例)。
添加maven依赖
我们在上一节中的admin-cloud-server中添加spring-boot邮件相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
修改properties文件配置
我们在application.properties文件中添加如下配置
#邮件通知配置
#邮件服务器地址
spring.mail.host=smtp.qq.com
#发送邮件的邮箱地址
spring.mail.username=test0@qq.com
#QQ邮箱授权码或163邮箱密码
spring.mail.password=******
#添加邮件认证相关设置
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
#需要忽略的状态改变通知,逗号分隔,例如不通知离线到上线的状态,则填写为OFFLINE:UP
#spring.boot.admin.notify.mail.ignore-changes=
#接收通知的邮箱地址,逗号分隔
spring.boot.admin.notify.mail.to=test2@qq.com
#需要抄送的邮箱地址,逗号分隔
spring.boot.admin.notify.mail.cc=test1@qq.com
#邮件发送者,大部分情况与登录名相同
spring.boot.admin.notify.mail.from=${spring.mail.username}
#邮件主题,默认是:#{application.name} (#{application.id}) is #{to.status}
spring.boot.admin.notify.mail.subject=#{application.name} (#{application.id}) is #{to.status}
#邮件内容,默认是:#{application.name} (#{application.id})\nstatus changed from #{from.status} to #{to.status}\n\n#{application.healthUrl}
spring.boot.admin.notify.mail.text=#{application.name} (#{application.id})\nstatus changed from #{from.status} to #{to.status}
测试效果
还是分别启动eureka-server、admin-cloud-server、admin-cloud-client程序。片刻之后我们重启admin-cloud-client
可以在windows任务栏的QQ弹出了admin-client-client状态变表更的邮件提示,我们进入邮箱可以看到两封邮件,效果如下:
我们点击一个邮件进去可以看到如下的默认邮件内容
可以看到邮件详细内容的格式与spring.boot.admin.notify.mail.text参数配置的格式相同,所以我们可以通过更改该配置的内容格式来定制我们想要的预警内容。
小结
通过spring-boot-admin集成邮件通知可以更加丰富监控程序的功能。为生产环境中的安全稳定运行又提供了一层有效的保障。
总结
spring-boot-admin为我们基于spring-boot开发的程序(包括spring-cloud)提供了安全端点的基础监控,对有异常状态(客户端状态变更为上线、下线、未知等)支持多种通知方式。为生产环境的运营维护提供了直观的UI效果。
当然对于当前复杂的生产环境来说,spring-boot-admin所支持的还是一下小部分。未来希望能够看到更多的更开放的监控功能的集成(例如zipkin),并且支持更多自定义的监控开发。
参考资料: