接着上篇文章介绍,上篇文章使用了Prometheus+Grafana+AlertManager实现了报警系统,并了解了其中的一些原理。原理就是Prometheus采集了SpringBoot Actuator暴露出的数据,并放到了Grafana做了一个数据可视化。
今天我们单拿出Tomcat和Druid来说,是因为需要一定的配置才能暴露到SpringBoot Actuator
SpringBoot版本:2.3.6.RELEASE web和actuator版本相同
一、监控tomcat
1.项目中Tomcat配置
server:
tomcat:
threads:
max: 500
min-spare: 20
accept-count: 1000
2.暴露到SpringBoot Actuator
因为SpringBoot已经内置了Tomcat ,需要开启一下配置就可以
server.tomcat.mbeanregistry.enabled=true
因为在上篇文章中发现Grafana的Tomcat板块没有内容,很纳闷,就去官网上找了下答案,发现需要开启一下配置 https://github.com/spring-projects/spring-boot/issues/24503
3.SpringBoot Actuator暴露出来的数据
4.Grafana展示页面
二、监控Druid
1.项目中的配置
druid:
initial-size: 5
min-idle: 10
max-active: 200
max-wait: 60000
min-evictable-idle-time-millis: 300000
time-between-eviction-runs-millis: 60000
filters: stat
validation-query: SELECT 1
test-on-borrow: true
test-on-return: true
test-while-idle: true
2.暴露到SpringBoot Actuator
SpringBoot没有实现暴露Druid数据的方法,所以需要自己实现了。参考下如何暴露Tomcat的数据,在TomcatMetrics类中
public class TomcatMetrics implements MeterBinder, AutoCloseable {
@Override
public void bindTo(MeterRegistry registry) {
registerGlobalRequestMetrics(registry);
registerServletMetrics(registry);
registerCacheMetrics(registry);
registerThreadPoolMetrics(registry);
registerSessionMetrics(registry);
}
private void registerSessionMetrics(MeterRegistry registry) {
Gauge.builder("tomcat.sessions.active.max", manager, Manager::getMaxActive)
.tags(tags)
.baseUnit(BaseUnits.SESSIONS)
.register(registry);
Gauge.builder("tomcat.sessions.active.current", manager, Manager::getActiveSessions)
.tags(tags)
.baseUnit(BaseUnits.SESSIONS)
.register(registry);
FunctionCounter.builder("tomcat.sessions.created", manager, Manager::getSessionCounter)
.tags(tags)
.baseUnit(BaseUnits.SESSIONS)
.register(registry);
FunctionCounter.builder("tomcat.sessions.expired", manager, Manager::getExpiredSessions)
.tags(tags)
.baseUnit(BaseUnits.SESSIONS)
.register(registry);
FunctionCounter.builder("tomcat.sessions.rejected", manager, Manager::getRejectedSessions)
.tags(tags)
.baseUnit(BaseUnits.SESSIONS)
.register(registry);
TimeGauge.builder("tomcat.sessions.alive.max", manager, TimeUnit.SECONDS, Manager::getSessionMaxAliveTime)
.tags(tags)
.register(registry);
}
}
可以看出SpringBoot预留了暴露数据的接口MeterBinder ,实现该接口中的bindTo 方法就可以暴露出数据了
我们模拟一下Tomcat的实现方法,实现Druid的。参考这位大佬的博客
@Slf4j
@Component
public class DruidDataSourceMetrics implements MeterBinder, ApplicationContextAware {
private ApplicationContext applicationContext;
public boolean collect(MeterRegistry meterRegistry) {
Map<String, DruidDataSource> datasources = applicationContext.getBeansOfType(DruidDataSource.class);
if (datasources.values().contains(null)) {
return false;
}
for (Map.Entry<String, DruidDataSource> entry : datasources.entrySet()) {
Gauge.builder("druid.pool.error.count",()->(entry.getValue().getErrorCount())).tags("datasourcename", entry.getKey()).description("druid pool error count")
.register(meterRegistry);
Gauge.builder("druid.pool.active.connections", () -> (entry.getValue().getActiveCount())).tags("datasourcename", entry.getKey()).description("druid pool active count")
.register(meterRegistry);
Gauge.builder("druid.pool.idle.connections", () -> (entry.getValue().getPoolingCount())).tags("datasourcename", entry.getKey()).description("druid pool idle count")
.register(meterRegistry);
Gauge.builder("druid.pool.wait.connection.thread", () -> (entry.getValue().getWaitThreadCount())).tags("datasourcename", entry.getKey()).description("druid pool wait thread count")
.register(meterRegistry);
Gauge.builder("druid.pool.sum.connections", () -> ((entry.getValue().getPoolingCount() + entry.getValue().getActiveCount()))).tags("datasourcename", entry.getKey()).description("druid pool sum count")
.register(meterRegistry);
}
return true;
}
public void bindTo(MeterRegistry meterRegistry) {
try {
Executors.newSingleThreadScheduledExecutor().schedule(() -> {
while (!collect(meterRegistry)) {
}
}, 5, TimeUnit.SECONDS);
} catch (Exception e) {
log.warn("druid datasource collected error{}",e);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
代码实现也比较简单,从IOC容器中拿到DruidDataSource的Bean,使用线程池每隔5s中去获取一下Bean中的属性值使用bindTo方法暴露出去
3.SpringBoot Actuator暴露出的数据
4.Grafana展示页面
关注的数据如下
- acive_connections:这一时刻连接池中活跃的连接有多少
- idle_connections:这一时刻连接池空闲的连接有多少
- wait_connection_thread:这一时刻有多少线程处于等待拿数据库连接状态
|