spring boot 源码解析37-CounterService详解

 2019-10-17 22:06  阅读(2499)
文章分类:Spring boot

前言

从本文开始,我们开始分析org.springframework.boot.actuate.metrics包中的实现.这里面的代码比较多,因此我们采用一步一步的方式来进行分析.本文先分析CounterService相关的类(DropwizardMetricServices这个实现我们后续分析) 类图如下:

20191017100450\_1.png

ps:关于CounterService的自动装配,我们在解析完GaugeService之后再介绍.

解析

CounterService

CounterService–> 1个可以增加,减少,重置的命名的计数器服务.其声明的方法如下:

// 计数器加1
    void increment(String metricName);

    // 计数器减1
    void decrement(String metricName);

    // 重置给定的技术器
    void reset(String metricName);

MetricWriter

MetricWriter继承自GaugeWriter,CounterWriter接口.没有声明其他的方法,只是1个合并接口.

GaugeWriter

GaugeWriter–>测量值的写出接口.声明了如下方法:

/** * Set the value of a metric. * @param value the value */
    void set(Metric<?> value);

Delta

Delta–> 1个可增长的测量值所对应的值对象(通常作为计数器).代码如下:

public class Delta<T extends Number> extends Metric<T> {

        public Delta(String name, T value, Date timestamp) {
            super(name, value, timestamp);
        }

        public Delta(String name, T value) {
            super(name, value);
        }

    }

CounterWriter

CounterWriter–>计数器的简单 writer 接口.声明了如下方法:

// 增加当前metric的值(或者减少,如果Delta 是负数的话). Delta 中指定的name指定了要增加的metric的名字
    void increment(Delta<?> delta);

    // 重置,通常会置为0.该操作是可选的(一些实现可能无法实现该契约--> 什么也没有做)
    void reset(String metricName);

SimpleInMemoryRepository

SimpleInMemoryRepository–> 在内存中存储数据的工具类.该类还是一个泛型类,其泛型参数T为存储类型

  1. 字段如下:

    // key-->metricName,value--> T
        private ConcurrentNavigableMap values = new ConcurrentSkipListMap();
    
        // key--> metricName,value-->Object(用于加锁)
        private final ConcurrentMap locks = new ConcurrentHashMap();
    
  2. 该类还声明1个接口–> Callback,作用是用来修改值的回调接口,其泛型参数T为值的类型.如下:

    public interface Callback {
    
            // 修改给定的值
            T modify(T current);
    
        }
    
  3. 声明了如下方法:

    1. getLock–>用于获得给定名称所对应的锁.代码如下:

      private Object getLock(String name) {
              Object lock = this.locks.get(name);
              if (lock == null) {
                  Object newLock = new Object();
                  lock = this.locks.putIfAbsent(name, newLock);
                  if (lock == null) {
                      lock = newLock;
                  }
              }
              return lock;
          }
      

      从缓存中获取,如果获取到则直接返回,如果获取不到则实例化1个Object放入缓存中,然后返回

    2. update–> 对指定的name所对应的值,调用传入的Callback进行修改,然后放入缓存中.代码如下:

      public T update(String name, Callback callback) {
              Object lock = getLock(name);
              synchronized (lock) {
                  T current = this.values.get(name);
                  T value = callback.modify(current);
                  this.values.put(name, value);
                  return value;
              }
          }
      
    3. findAllWithPrefix–> 通过指定的前缀进行查找.代码如下:

      public Iterable findAllWithPrefix(String prefix) {
              if (prefix.endsWith(".*")) {
                  prefix = prefix.substring(0, prefix.length() - 1);
              }
              if (!prefix.endsWith(".")) {
                  prefix = prefix + ".";
              }
              return new ArrayList(
                      // 不包括头,含为-->(xx]
                      this.values.subMap(prefix, false, prefix + "~", true).values());
          }
      
      1. 如果prefix是.* 结尾的,则进行截取.如prefix为aa.*,则会截取为aa.
      2. 如果prefix不是.结尾的,则为其加上.
      3. 获得value中key在prefix到prefix~范围中的值,左开右闭

    还有其他的方法,比较简单,这里就不在赘述了

InMemoryMetricRepository

1个在内存中存储metrics的MetricRepository的实现

  1. 字段如下:

    private final SimpleInMemoryRepository> metrics = new SimpleInMemoryRepository>();
    
  2. 其接口的方法实现如下:

    1. increment,代码如下:

      public void increment(Delta delta) {
              final String metricName = delta.getName();
              final int amount = delta.getValue().intValue();
              final Date timestamp = delta.getTimestamp();
              this.metrics.update(metricName, new Callback>() {
      
                  @Override
                  public Metric modify(Metric current) {
                      if (current != null) {
                          return new Metric(metricName,
                                  current.increment(amount).getValue(), timestamp);
                      }
                      return new Metric(metricName, (long) amount, timestamp);
                  }
      
              });
          }
      
      1. 从metrics中获得Delta所对应的Metric
      2. 如果Metric存在的话,则直接实例化1个Metric,名字,时间戳不变,值则在原先的基础上加上传入的Delta的值
      3. 如果不存在,则直接根据传入Delta的名称,时间戳,值实例化1个Metric,加入到metrics的values中
    2. set 实现如下:

      public void set(Metric value) {
              this.metrics.set(value.getName(), value);
          }
      

      调用SimpleInMemoryRepository的 set方法直接保存至SimpleInMemoryRepository持有的values中.代码如下:

      public void set(String name, T value) {
              this.values.put(name, value);
          }
      
    3. count–>返回SimpleInMemoryRepository中持有的values的大小.实现如下:

      public long count() {
              return this.metrics.count();
          }
      

      调用:

      public long count() {
              return this.values.size();
          }
      
    4. reset–> 从SimpleInMemoryRepository中的values删除.代码如下:

      public void reset(String metricName) {
              this.metrics.remove(metricName);
          }
      

      调用:

      public void remove(String name) {
              this.values.remove(name);
          }
      
    5. findOne–>从SimpleInMemoryRepository中的values中查找,代码如下:

      public Metric findOne(String metricName) {
              return this.metrics.findOne(metricName);
          }
      

      调用:

      public T findOne(String name) {
              return this.values.get(name);
          }
      
    6. findAll –> 获得SimpleInMemoryRepository中values的所有值.代码如下:

      public Iterable> findAll() {
              return this.metrics.findAll();
          }
      
    7. findAllWithPrefix–> 从SimpleInMemoryRepository的values中查找前缀为prefix的Metric.代码如下:

      public Iterable> findAllWithPrefix(String prefix) {
              return this.metrics.findAllWithPrefix(prefix);
          }
      
  3. 自动装配:

    在LegacyMetricRepositoryConfiguration中进行了装配.代码如下:

    @Configuration
        @ConditionalOnJava(value = JavaVersion.EIGHT, range = Range.OLDER_THAN)
        @ConditionalOnMissingBean(name = "actuatorMetricRepository")
        static class LegacyMetricRepositoryConfiguration {
    
            @Bean
            @ExportMetricReader
            @ActuatorMetricWriter
            public InMemoryMetricRepository actuatorMetricRepository() {
                return new InMemoryMetricRepository();
            }
    
            .....
        }
    

    当满足以下条件时生效:

    • @ConditionalOnJava(value = JavaVersion.EIGHT, range = Range.OLDER_THAN) –> 在jdk1.8之前的运行环境下运行
    • @ConditionalOnMissingBean(name = “actuatorMetricRepository”)–> beanFactory中不存在id为actuatorMetricRepository的bean

    其中: @ExportMetricReader 如下:

    @Qualifier
        @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
                ElementType.ANNOTATION_TYPE })
        @Retention(RetentionPolicy.RUNTIME)
        @Inherited
        @Documented
        public @interface ExportMetricReader {
        }
    

    @ActuatorMetricWriter如下:

    @Qualifier
        @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
                ElementType.ANNOTATION_TYPE })
        @Retention(RetentionPolicy.RUNTIME)
        @Inherited
        @Documented
        public @interface ActuatorMetricWriter {
        }
    

DefaultCounterService

DefaultCounterService –> 在jdk1.8之前默认装配的CounterService的实现

  1. 字段,构造器如下:

    // 此时注入的是InMemoryMetricRepository
        private final MetricWriter writer;
    
        // key-->metricName,value-->加入前缀后的metricName
        private final ConcurrentHashMap names = new ConcurrentHashMap();
    
        public DefaultCounterService(MetricWriter writer) {
            this.writer = writer;
        }
    
  2. 方法实现如下:

    1. increment,代码如下:

      public void increment(String metricName) {
              this.writer.increment(new Delta(wrap(metricName), 1L));
          }
      
      1. 尝试为metricName加上前缀.代码如下:

        private String wrap(String metricName) {
                String cached = this.names.get(metricName);
                if (cached != null) {
                    return cached;
                }
                if (metricName.startsWith("counter.") || metricName.startsWith("meter.")) {
                    return metricName;
                }
                String name = "counter." + metricName;
                this.names.put(metricName, name);
                return name;
            }
        
        1. 如果缓存中有的话,则直接返回对应的值
        2. 如果传入的metricName是counter.或者meter.开头的则直接返回
        3. 为metricName 加上counter.的前缀,放入到names缓存中,然后进行返回
      2. 实例化Delta

      3. 加入到InMemoryMetricRepository中

    2. decrement,代码如下:

      public void decrement(String metricName) {
              this.writer.increment(new Delta(wrap(metricName), -1L));
          }
      
    3. reset,代码如下:

      public void reset(String metricName) {
              this.writer.reset(wrap(metricName));
          }
      

Buffer

Buffer,抽象泛型类–>可变的buffer(含有时间戳和所对应的值)的基类.泛型参数为T extends Number–>其持有值的类型.代码如下:

abstract class Buffer<T extends Number> {

        private volatile long timestamp;

        Buffer(long timestamp) {
            this.timestamp = timestamp;
        }

        public long getTimestamp() {
            return this.timestamp;
        }

        public void setTimestamp(long timestamp) {
            this.timestamp = timestamp;
        }

        /** * Returns the buffer value. * @return the value of the buffer */
        public abstract T getValue();

    }

CounterBuffer

CounterBuffer–> 继承自Buffer,泛型参数为Long.其类上声明了@UsesJava8注解,表明该类是使用java8特有的api进行实现的,不意味着其严格要求java 8. 代码如下:

@UsesJava8// 表明该类是使用java8特有的api进行实现的,不意味着其严格要求java 8 
    public class CounterBuffer extends Buffer<Long> {

        private final LongAdder adder;

        public CounterBuffer(long timestamp) {
            super(timestamp);
            this.adder = new LongAdder();
        }

        public void add(long delta) {
            this.adder.add(delta);
        }

        public void reset() {
            this.adder.reset();
        }

        @Override
        public Long getValue() {
            return this.adder.sum();
        }

    }

关于LongAdder,可以参考如下链接:

Java8 更快的原子类:LongAdder(笔记)

Java 8 LongAdders:管理并发计数器的正确方式

Buffers

Buffers–> 管理1个Buffer对象的映射的抽象泛型基类.泛型参数为B extends Buffer

CounterBuffers

CounterBuffers–> 继承自Buffers,泛型参数为CounterBuffer.

  1. createBuffer–> 直接实例化了CounterBuffer.代码如下:

    protected CounterBuffer createBuffer() {
            return new CounterBuffer(0);
        }
    
  2. 此外,还声明了2个方法.如下:

    1. increment–>对给定名字的CounterBuffer增长给定的幅度. 代码如下:

      public void increment(final String name, final long delta) {
              doWith(name, new Consumer() {
      
                  @Override
                  public void accept(CounterBuffer buffer) {
                      buffer.setTimestamp(System.currentTimeMillis());
                      buffer.add(delta);
                  }
      
              });
          }
      
      1. 从父类中的buffers获得给定名字所对应的CounterBuffer,如果不存在,则创建1个
      2. 将CounterBuffer中的时间戳设为当前时间,并增加指定的步幅.
    2. reset–> 对给定名字的CounterBuffer进行重置.代码如下:

      public void reset(final String name) {
              doWith(name, new Consumer() {
      
                  @Override
                  public void accept(CounterBuffer buffer) {
                      buffer.setTimestamp(System.currentTimeMillis());
                      buffer.reset();
                  }
      
              });
          }
      
      1. 从父类中的buffers获得给定名字所对应的CounterBuffer,如果不存在,则创建1个
      2. 将CounterBuffer中的时间戳设为当前时间,将其值设置为0
  3. 自动装配:

    在FastMetricServicesConfiguration中进行了配置.代码如下:

    @Configuration
        @ConditionalOnJava(JavaVersion.EIGHT)
        @ConditionalOnMissingBean(GaugeService.class)
        static class FastMetricServicesConfiguration {
    
            @Bean
            @ConditionalOnMissingBean
            public CounterBuffers counterBuffers() {
                return new CounterBuffers();
            }
        }
    

    当满足如下条件时该配置生效:

    1. @ConditionalOnJava(JavaVersion.EIGHT)–> 在jdk1.8的环境中运行
    2. @ConditionalOnMissingBean(GaugeService.class)–>BeanFactory中不存在GaugeService类型的bean时生效
    3. @ConditionalOnMissingBean–>BeanFactory中不存在CounterBuffers类型的bean时生效

BufferCounterService

BufferCounterService–>实现了CounterService接口.

  1. 字段,构造器如下:

    // key --> 原始的名字,value --> buffer中的名字
        private final ConcurrentHashMap names = new ConcurrentHashMap();
    
        private final CounterBuffers buffers;
    
        public BufferCounterService(CounterBuffers buffers) {
            this.buffers = buffers;
        }
    
  2. 方法实现如下:

    1. increment,代码如下:

      public void increment(String metricName) {
              this.buffers.increment(wrap(metricName), 1L);
          }
      
      1. 尝试为metricName加上前缀.代码如下:

        private String wrap(String metricName) {
                String cached = this.names.get(metricName);
                if (cached != null) {
                    return cached;
                }
                if (metricName.startsWith("counter.") || metricName.startsWith("meter.")) {
                    return metricName;
                }
                String name = "counter." + metricName;
                this.names.put(metricName, name);
                return name;
            }
        
        1. 如果缓存中有的话,则直接返回对应的值
        2. 如果传入的metricName是counter.或者meter.开头的则直接返回
        3. 为metricName 加上counter.的前缀,放入到names缓存中,然后进行返回
      2. 加入到CounterBuffers中

    2. decrement,代码如下:

      public void decrement(String metricName) {
              this.buffers.increment(wrap(metricName), -1L);
          }
      
    3. reset,代码如下:

      public void reset(String metricName) {
              this.buffers.reset(wrap(metricName));
          }
      

CounterService使用案例

我们可以使用CounterService来完成对接口请求次数的统计.

  1. 新建CounterController,代码如下:

    package com.example.demo;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.boot.actuate.metrics.CounterService;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.RestController;
        @RestController
        public class CounterController {
    
            @Autowired
            private CounterService counterService;
    
            @RequestMapping("/test-counter")
            public String testCounter() {
    
                counterService.increment("test-counter.count");
    
                return "操作成功";
            }
        }
    
  2. 我们访问如下链接后 http://127.0.0.1:8080/test-counter 后,访问 http://127.0.0.1:8080/metrics,即可发现counter.test-counter.count对应的统计次数为1.如下:

    counter.test-counter.count: 1
    

来源:[]()

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> spring boot 源码解析37-CounterService详解

相关推荐