Java项目集成grafana_Atlas+Spectator+Grafana搭建实时监控平台

本文介绍了如何将Java项目与Grafana集成,利用Atlas和Spectator实现实时监控。Atlas作为metrics后端,Spectator负责数据收集,Grafana则用于展示数据。通过Spectator的metrics数据发送到Atlas,再由Grafana配置数据源获取并绘制监控图表。文章详细讲解了各个组件的功能、集成步骤,以及监控JVM内存和GC的相关指标。
摘要由CSDN通过智能技术生成

平台组成

Atlas

Netflix开源的管理多维时序数据的metrics后端服务系统。可以用于汇总存储基于Spectator库收集到的metrics数据,并提供强大的查询语法,支持图表,json, 图片等格式返回。

Spectator

Netflix开源的用于收集metrics的lib库, 主要为了支持JDK8, 而用于替换同类旧产品Servo的项目。

Grafana

Grafana是灵活的Dashboard开源项目,可通过简单的配置自动画出对应数据源的图形。常用于实时监控系统的展示功能。

组合原理

应用通过Spectator收集metrics,并通过Atlas-client发送给Atlas, Grafana配置Atlas数据源和监控数据,实时从Atlas中获取时序数据。

搭建环境

Atlas

直接部署Netflix开源的Atlas,可独立运行的java包。由于需要grafana3-atlas-datasource的支持,同时为了更好的支持atlas json数据,需要Atlas 1.5.0+以上版本,官方release页面https://github.com/Netflix/atlas/releases 1.5.0+版本还未release。需要大家自行根据最新master代码编译打包。

$ curl -Lo memory.conf https://raw.githubusercontent.com/Netflix/atlas/master/conf/memory.conf

java -jar standalone-master.jar memory.conf

Grafana

安装atals插件

感谢briangann提供的3.X插件(之前我测试使用的2.6版的插件),在3.X插件基础上我增加了对Grafana模板功能的支持,暂时还没和原版合并,大家可以自行选择。

git clone https://github.com/jewelknife/grafana3-atlas-datasource.git

mv grafana3-atlas-datasource /var/lib/grafana/plugins

# 重启grafana

程序新增依赖

默认应用程序已经集成springboot, springcloud。

// GC和JVM等扩展功能需要增加额外jar包,详见后面

org.springframework.cloud

spring-cloud-starter-spectator

org.springframework.boot

spring-boot-starter-actuator

org.springframework.cloud

spring-cloud-starter-atlas

增加全局tag配置

// 默认增加spring.application.name

@Configuration

public class AtlasTagProviderConfigration {

@Bean

AtlasTagProvider atlasCommonTags(@Value("${spring.application.name}") String appName) {

return () -> Collections.singletonMap("app", appName);

}

}

开启Atlas-client push功能

增加注解

@EnableAtlas

具体功能实现

TPS

spring-cloud-neflix-core 自带mvc接收请求,restful请求,httpclient请求等metrics记录功能。

其中记录mvc接受请求的拦截器为:

MetricsHandlerInterceptor.java

public class MetricsHandlerInterceptor extends HandlerInterceptorAdapter {

@Value("${netflix.metrics.rest.metricName:rest}")

String metricName;

@Value("${netflix.metrics.rest.callerHeader:#{null}}")

String callerHeader;

@Autowired

MonitorRegistry registry;

@Autowired

ServoMonitorCache servoMonitorCache;

@Autowired

Collection tagProviders;

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response,

Object handler) throws Exception {

RequestContextHolder.getRequestAttributes().setAttribute("requestStartTime",

System.nanoTime(), SCOPE_REQUEST);

return super.preHandle(request, response, handler);

}

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response,

Object handler, Exception ex) throws Exception {

RequestContextHolder.getRequestAttributes().setAttribute("exception", ex,

SCOPE_REQUEST);

Long startTime = (Long) RequestContextHolder.getRequestAttributes().getAttribute(

"requestStartTime", SCOPE_REQUEST);

if (startTime != null)

recordMetric(request, response, handler, startTime);

super.afterCompletion(request, response, handler, ex);

}

protected void recordMetric(HttpServletRequest request, HttpServletResponse response,

Object handler, Long startTime) {

String caller = null;

if (callerHeader != null) {

caller = request.getHeader(callerHeader);

}

SmallTagMap.Builder builder = SmallTagMap.builder();

for (MetricsTagProvider tagProvider : tagProviders) {

Map tags = tagProvider.httpRequestTags(request, response,

handler, caller);

for (Map.Entry tag : tags.entrySet()) {

builder.add(Tags.newTag(tag.getKey(), tag.getValue()));

}

}

MonitorConfig.Builder monitorConfigBuilder = MonitorConfig.builder(metricName);

monitorConfigBuilder.withTags(builder);

servoMonitorCache.getTimer(monitorConfigBuilder.build()).record(

System.nanoTime() - startTime, TimeUnit.NANOSECONDS);

}

}

DefaultMetricsTagProvider.java

@Override

public Map httpRequestTags(HttpServletRequest request,

HttpServletResponse response, Object handler, String caller) {

Map tags = new HashMap<>();

tags.put("method", request.getMethod());

tags.put("status", ((Integer) response.getStatus()).toString());

String uri = (String) request

.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);

if (uri == null) {

uri = request.getPathInfo();

}

if (!StringUtils.hasText(uri)) {

uri = "/";

}

uri = sanitizeUrlTemplate(uri.substring(1));

tags.put("uri", uri.isEmpty() ? "root" : uri);

Object exception = request.getAttribute("exception");

if (exception != null) {

tags.put("exception", exception.getClass().getSimpleName());

}

if (caller != null) {

tags.put("caller", caller);

}

return tags;

}

上述代码可以看出,SpringCloud主要用Timer来记录Http请求,默认metricName为rest,可以通过配置"netflix.metrics.rest.metricName"进行修改。同时tags里增添了method,uri, status。 所以配置数据条件如下即可

eb94b451a0040af57ba27935f551083d.png

Atlas里画出的图如下:

26eedeaccd8f428351cc7e40358e2b19.png

JVM内存相关

集成步骤

增加依赖

com.netflix.spectator:spectator-ext-jvm:0.40.0

初始化

import com.netflix.spectator.jvm.Jmx;

@Configuration

public class JvmMonitorConfigration {

@Autowired

public void setRegistry(Registry registry) {

Jmx.registerStandardMXBeans(registry);

}

}

Metrics

jvm.memory.used

当前使用的内存大小, 单位为bytes, 根据tags可以区分出6种:(这里有个注意点,tag的value存在空格,atlas和Spectator默认不支持)

atlas.dstype=rate,id=Code Cache,memtype=NON_HEAP,policy=DefaultPublishingPolicy

atlas.dstype=rate,id=Compressed Class Space,memtype=NON_HEAP,policy=DefaultPublishingPolicy

atlas.dstype=rate,id=Metaspace,memtype=NON_HEAP,policy=DefaultPublishingPolicy

atlas.dstype=rate,id=PS Eden Space,memtype=HEAP,policy=DefaultPublishingPolicy

atlas.dstype=rate,id=PS Old Gen,memtype=HEAP,policy=DefaultPublishingPolicy

atlas.dstype=rate,id=PS Survivor Space,memtype=HEAP,policy=DefaultPublishingPolicy

如上,可以根据id和memtype过滤指定"jvm.memory.used"值,memtype表示内存类型,它只有两个值HEAP和NON_HEAP。

jvm.memory.committed

当前可使用的内存大小(包括已使用的),单位为bytes,分类同上。(>=used) committed不足时jvm向系统申请,若超过max则发生OutOfMemoryError错误。

jvm.memory.max

最大可使用内存,单位为bytes,分类同上。(>=committed)

GC相关

集成步骤

增加依赖

com.netflix.spectator:spectator-ext-gc:0.40.0

初始化GCLogger

@Configuration

public class JvmMonitorConfigration {

// Keep a single instance of the logger

private GcLogger gc;

@Autowired

public void setRegistry(Registry registry) {

Spectator.globalRegistry().add(registry);

gc = new GcLogger();

gc.start(null);

}

}

Metrics

jvm.gc.allocationRate

年轻代GC回收内存速率,单位为bytes/second。回收的内存大小为youngGen.sizeBeforeGC - youngGen.sizeAfterGC。

jvm.gc.promotionRate

年轻代转移老年代速率,单位为bytes/second。转移的内存大小为abs(oldGen.sizeAfterGC - oldGen.sizeBeforeGC)

jvm.gc.liveDataSize

Full GC后老年代存活对象的大小,单位为bytes。

jvm.gc.maxDataSize

老年代最大大小,单位为bytes。

jvm.gc.pause

GC事件暂停时间,单位为:

statistic=max: seconds

statistic=count: events/second

statistic=totalTime: seconds/second

监控参考

feb1ba3d6ec903ffdc24c705392aede6.png

遇到的问题

metrics长度过长被拒绝的问题

metrics tags的value效验不通过问题:

比如jvm的metrics中的value会有空格,而默认校验表达式为[\.\-\w]+,它不支持空格。client和server端都会效验, client端校验功能需要反射修改效验正则表达式,服务端可以通过配置去掉校验功能。

"timerCache is above the warning threshold of 1000 with size XXX"日志告警

这个告警主要是说创建的timer已经超过默认阈值1000了,可以通过增大配置netflix.metrics.servo.cacheWarningThreshold来解决。

参考文档

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值