使用Spring Boot时,通过Caffeine缓存获取指标时,可以使用Prometheus来获取

验证版本

Spring Boot 2.1.0.RELEASE 的释放版本。

使用Spring Boot来使用缓存

首先进行缓存的设置。要做的是

spring-boot-starter-cache の依存を入れる
Cacheableなmethodを書く。これはControllerに置かない。別のComponentに置く。
ConfigurationクラスかApplicationクラスに@EnableCachingをつける

三点

加入 spring-boot-starter-cache 的依赖

如果使用Gradle,可以在build.gradle文件的dependencies部分进行指定。为了创建一个简单的web布局,可以按照以下方式定义依赖关系。

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-actuator')
    implementation('org.springframework.boot:spring-boot-starter-cache')
    implementation('org.springframework.boot:spring-boot-starter-web')
    compileOnly('org.projectlombok:lombok')
    testImplementation('org.springframework.boot:spring-boot-starter-test')
}

定义可缓存的方法。

适当地准备一个Repository类,并按照以下方式实现,从缓存中取出一次回显的数值。 @Cacheable的value被设置为cacheName。

@Slf4j
@Repository
public class EchoRepository {

    @Cacheable("echo")
    public String echo(String value) {
        log.info("no cache: {}", value);
        return value;
    }
}

此外,还需要实现调用此处理的RestController控制器。

@RequiredArgsConstructor
@RestController
public class EchoController {

    private final EchoRepository repository;

    @GetMapping("echo")
    public String echo(@RequestParam  String value) {
        return repository.echo(value);
    }
}

在Configuration类或Application类中加上@EnableCaching注解。

暂时将其附加在Application类中。

@EnableCaching
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

考试

使用两个参数进行两次调用。

$ curl http://localhost:8080/echo?value=test
test$ 
$ curl http://localhost:8080/echo?value=test
test$ 
$ curl http://localhost:8080/echo?value=test1
test1$ 
$ curl http://localhost:8080/echo?value=test1
test1$ 

查看日志

2018-10-31 23:41:26.824  INFO 3663 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 18 ms
2018-10-31 23:41:26.914  INFO 3663 --- [nio-8080-exec-1] c.k.demo.repository.EchoRepository       : no cache: test
2018-10-31 23:41:34.690  INFO 3663 --- [nio-8080-exec-3] c.k.demo.repository.EchoRepository       : no cache: test1

似乎已经缓存了

使用咖啡因

在这里,缓存实现是可以选择的。默认情况下,似乎会选择使用基于ConcurrentHashMap的简单实现,但我们这里使用Caffeine。

只需添加implementation(‘com.github.ben-manes.caffeine:caffeine’)到依赖项中,即可使用。

收集咖啡因的度量指标

在微测中实现了收集Caffeine指标的实现,我想要利用它。

使用Spring Boot actuator提供Prometheus的终端点。

首先,在dependencies中添加implementation(‘io.micrometer:micrometer-registry-prometheus’)。然后,在application.yml中添加management.endpoints.web.exposure.include: prometheus,以便可以访问prometheus的端点,并可以使用curl尝试发送请求,获取默认的指标和由AutoConfiguration注册的指标。请注意,actuator的基本路径由management.endpoints.web.base-path定义,默认为/actuator。

$ curl http://localhost:8080/actuator/prometheus 2>/dev/null | head
# HELP tomcat_global_request_seconds  
# TYPE tomcat_global_request_seconds summary
tomcat_global_request_seconds_count{name="http-nio-8080",} 4.0
tomcat_global_request_seconds_sum{name="http-nio-8080",} 0.597
# HELP tomcat_threads_config_max_threads  
# TYPE tomcat_threads_config_max_threads gauge
tomcat_threads_config_max_threads{name="http-nio-8080",} 200.0
# HELP tomcat_sessions_active_current_sessions  
# TYPE tomcat_sessions_active_current_sessions gauge
tomcat_sessions_active_current_sessions 0.0

添加设置以收集咖啡因的度量标准。

在application.yml中定义spring.cache.caffeine.spec: maximumSize=500,expireAfterAccess=600s,recordStats可以将recordStats参数放入spring.cache.caffeine.spec中。在指标收集方面,只需要recordStats即可,但其他可配置的值可以参考CaffeineSpec.java。

我原本以为会从这里开始取得…

在调用先前实现的/echo后,调用prometheus的端点。

$ curl http://localhost:8080/echo?value=test
test$ 
$ curl http://localhost:8080/actuator/prometheus 2>/dev/null | grep cache
# HELP tomcat_cache_access_total  
# TYPE tomcat_cache_access_total counter
tomcat_cache_access_total 0.0
# HELP tomcat_cache_hit_total  
# TYPE tomcat_cache_hit_total counter
tomcat_cache_hit_total 0.0

…请将以下内容用中文进行改写,只需要一种选项:

spring.cache.cache-names 的配置

看起来,根据CacheMetricsRegistrar的实现,如果不事先将静态使用的缓存cache的名称写在application.yml中,就无法被Micrometer注册。因此,与缓存相关的设置应该在application.yml中进行。

spring.cache:
  cache-names: echo
  caffeine.spec: maximumSize=500,expireAfterAccess=600s,recordStats

确保如此。再次确认后可获得。

]$ curl http://localhost:8080/actuator/prometheus 2>/dev/null | grep cache
# HELP cache_eviction_weight The sum of weights of evicted entries. This total does not include manual invalidations.
# TYPE cache_eviction_weight gauge
cache_eviction_weight{cache="echo",cacheManager="cacheManager",name="echo",} 0.0
# HELP cache_gets_total the number of times cache lookup methods have returned an uncached (newly loaded) value, or null
# TYPE cache_gets_total counter
cache_gets_total{cache="echo",cacheManager="cacheManager",name="echo",result="miss",} 1.0
cache_gets_total{cache="echo",cacheManager="cacheManager",name="echo",result="hit",} 0.0
# HELP tomcat_cache_hit_total  
# TYPE tomcat_cache_hit_total counter
tomcat_cache_hit_total 0.0
# HELP cache_size The number of entries in this cache. This may be an approximation, depending on the type of cache.
# TYPE cache_size gauge
cache_size{cache="echo",cacheManager="cacheManager",name="echo",} 1.0
# HELP cache_evictions_total cache evictions
# TYPE cache_evictions_total counter
cache_evictions_total{cache="echo",cacheManager="cacheManager",name="echo",} 0.0
# HELP cache_puts_total The number of entries added to the cache
# TYPE cache_puts_total counter
cache_puts_total{cache="echo",cacheManager="cacheManager",name="echo",} 0.0
# HELP tomcat_cache_access_total  
# TYPE tomcat_cache_access_total counter
tomcat_cache_access_total 0.0

我想要动态注册由@Cachable指定的缓存的指标。

使用@SpringCacheable指定的缓存名称写在spring.cache.cache-names中会变得琐碎和繁琐,而且当缓存数量增加时容易出现遗漏。如果想要实现这样的功能,虽然有些笨拙,但可以创建一个继承自CaffeineCacheManager的派生类,在CaffeineCache创建时将其注册到MeterRegistry中。(但不建议这样做…)

@RequiredArgsConstructor
public class InstrumentedCaffeineCacheManager extends CaffeineCacheManager {

    private final MeterRegistry meterRegistry;

    @Override
    protected Cache<Object, Object> createNativeCaffeineCache(String name) {
        Cache<Object, Object> nativeCache = super.createNativeCaffeineCache(name);
        CaffeineCacheMetrics.monitor(meterRegistry, nativeCache, name, Collections.emptyList());
        return nativeCache;
    }
}

将此注册为Bean。但是,在CaffeineCacheConfiguration中,除了简单地生成Bean之外,还需要进行其他处理,因此进行拷贝。

@EnableConfigurationProperties(CacheProperties.class)
@EnableCaching // Application classからこちらに移動した
@Configuration
@RequiredArgsConstructor
public class CacheConfig {

    private final MeterRegistry meterRegistry;

    private final CacheProperties cacheProperties;

    // CaffeineCacheConfigurationの実装をcopy
    @Bean
    public CaffeineCacheManager cacheManager() {
        CaffeineCacheManager cacheManager = createCacheManager();
        List<String> cacheNames = cacheProperties.getCacheNames();
        if (!CollectionUtils.isEmpty(cacheNames)) {
            cacheManager.setCacheNames(cacheNames);
        }
        return cacheManager;
    }
    // ここで独自CaffeineCacheManagerを生成
    private CaffeineCacheManager createCacheManager() {
        CaffeineCacheManager cacheManager = new InstrumentedCaffeineCacheManager(meterRegistry);
        setCacheBuilder(cacheManager);
        return cacheManager;
    }

    private void setCacheBuilder(CaffeineCacheManager cacheManager) {
        String specification = cacheProperties.getCaffeine().getSpec();
        if (StringUtils.hasText(specification)) {
            cacheManager.setCacheSpecification(specification);
        }
    }
}

这样,即使不在application.yml中写入spring.cache.cache-names,也能够动态地注册带有@Cacheable标记的缓存的指标。

bannerAds