Atlas+Spectator+Grafana搭建实时监控平台

转载请注明原创地址:http://my.oschina.net/u/2408085/blog/733900

平台组成

Atlas

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

Spectator

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

具体用法参考http://netflix.github.io/spectator/en/latest/intro/counter/

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

打开http://grafana.org/download/页面,自行下载安装最新Grafana v3.X版本。

安装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包,详见后面

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-spectator</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-atlas</artifactId>
</dependency>

增加全局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<MetricsTagProvider> 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<String, String> tags = tagProvider.httpRequestTags(request, response,
              handler, caller);
        for (Map.Entry<String, String> 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<String, String> httpRequestTags(HttpServletRequest request,
     HttpServletResponse response, Object handler, String caller) {
  Map<String, String> 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。 所以配置数据条件如下即可

输入图片说明

Atlas里画出的图如下:

输入图片说明

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

监控参考

输入图片说明

遇到的问题

metrics长度过长被拒绝的问题

比如hystrix相关的metrics都很长。https://github.com/spring-cloud/spring-cloud-netflix/issues/798 按照Issue中回答修改配置可解决。

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来解决。

参考文档

转载于:https://my.oschina.net/u/2408085/blog/733900

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spectator 是记录多维时间序列的简单插装代码库,要求 Java 7 及以上版本。代码示例:// The Spectator class provides a static lookup for the default registry Server s = new Server(Spectator.registry()); public class Server {   private final ExtendedRegistry registry;   private final Id requestCountId;   private final Timer requestLatency;   private final DistributionSummary responseSizes;   // We can pass in the registry to make unit testing easier. Outside of tests it should typically   // be bound to Spectator.registry() to make sure data goes to the right place.   public Server(ExtendedRegistry registry) {     this.registry = registry;     // Create a base id for the request count. The id will get refined with additional dimensions     // when we receive a request.     requestCountId = registry.createId("server.requestCount");     // Create a timer for tracking the latency. The reference can be held onto to avoid     // additional lookup cost in critical paths.     requestLatency = registry.timer("server.requestLatency");     // Create a distribution summary meter for tracking the response sizes.     responseSizes = registry.distributionSummary("server.responseSizes");     // Gauge type that can be sampled. In this case it will invoke the specified method via     // reflection to get the value. The registry will keep a weak reference to the object passed     // in so that registration will not prevent garbage collection of the server object.     registry.methodValue("server.numConnections", this, "getNumConnections");   }   public Response handle(Request req) {     final long s = System.nanoTime();     try {       Response res = doSomething(req);       // Update the counter id with dimensions based on the request. The counter will then       // be looked up in the registry which should be fairly cheap, such as lookup of id object       // in a ConcurrentHashMap. However, it is more expensive than having a local variable set       // to the counter.       final Id cntId = requestCountId         .withTag("country", req.country())         .withTag("status", res.status());       registry.counter(cntId).increment();       responseSizes.record(res.body().size());       return res;     } catch (Exception e) {       final Id cntId = requestCountId         .withTag("country", req.country())         .withTag("status", "exception")         .withTag("error", e.getClass().getSimpleName());       registry.counter(cntId).increment();       throw e;     } finally {       // Update the latency timer. This should typically be done in a finally block.       requestLatency.record(System.nanoTime() - s, TimeUnit.NANOSECONDS);     }   }   public int getNumConnections() {     // however we determine the current number of connections on the server   } } 标签:Netflix

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值