【问题标题】:FF4J: server side caching for feature storeFF4J:特征存储的服务器端缓存
【发布时间】:2021-04-08 20:46:38
【问题描述】:

我正在使用 ff4j-spring-boot-starterf4j-store-springjdbc 来设置我的 ff4j 服务器。我的所有其他微服务都使用 ff4j 提供的端点来访问此功能存储,并且响应以 JSON 格式返回。

由于数据不会经常更改并且我们正在尝试保存不必要的数据库调用,因此我正在尝试在服务器上缓存我的功能存储。此外,如果功能标志 DB 已关闭(用于刷新/维护),我们仍然希望其他服务能够使用缓存中的这些值成功启动。

我们在 pom.xml 中导入了ff4j-store-ehcache

        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.RC2</spring-cloud.version>
        <ff4j.version>1.8.7</ff4j.version>
        <odbc.version>19.6.0.0.0</odbc.version>
    </properties>
    <dependency>
            <groupId>org.ff4j</groupId>
            <artifactId>ff4j-spring-boot-starter</artifactId>
            <version>${ff4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.ff4j</groupId>
            <artifactId>ff4j-store-springjdbc</artifactId>
            <version>${ff4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.ff4j</groupId>
            <artifactId>ff4j-web</artifactId>
            <version>${ff4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.ff4j</groupId>
            <artifactId>ff4j-store-ehcache</artifactId>
            <version>${ff4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.ff4j</groupId>
            <artifactId>ff4j-webapi-jersey2x</artifactId>
            <version>1.8.11</version>
        </dependency>
        <!-- FF4J dependencies - end -->

我们的 FeatureCacheProviderEhCache 实现在 FF4jConfig.java 中看起来像这样。

@ConditionalOnClass({​​​​ ConsoleServlet.class, FF4jDispatcherServlet.class }​​​​)
public class Ff4jConfig extends SpringBootServletInitializer {​​​​

    @Autowired
    private DataSource dataSource;

    @Bean
    public ServletRegistrationBean<FF4jDispatcherServlet> ff4jDispatcherServletRegistrationBean(
            FF4jDispatcherServlet ff4jDispatcherServlet) {​​​​
        ServletRegistrationBean<FF4jDispatcherServlet> bean = new ServletRegistrationBean<FF4jDispatcherServlet>(
                ff4jDispatcherServlet, "/web-console/*");
        bean.setName("ff4j-console");
        bean.setLoadOnStartup(1);
        return bean;
    }​​​​

    @Bean
    @ConditionalOnMissingBean
    public FF4jDispatcherServlet getFF4jDispatcherServlet() {​​​​
        FF4jDispatcherServlet ff4jConsoleServlet = new FF4jDispatcherServlet();
        ff4jConsoleServlet.setFf4j(getFF4j());
        return ff4jConsoleServlet;
    }​​​​

    @Bean
    public FF4j getFF4j() {​​​​
        FF4j ff4j = new FF4j();
        FF4JCacheManager cacheManager = new FeatureCacheProviderEhCache();
        ff4j.setPropertiesStore(new PropertyStoreSpringJdbc(dataSource));
        ff4j.setFeatureStore(new FeatureStoreSpringJdbc(dataSource));
        ff4j.setEventRepository(new EventRepositorySpringJdbc(dataSource));
        ff4j.cache(cacheManager);
        

        // Enable audit mode
        ff4j.audit(true);

        return ff4j;
    }​​​​

}​​​​

WebConsole 会在数据库更改提交后立即反映它们,并且看起来缓存没有被命中,或者缓存中没有存储任何数据。

我希望能够使用此缓存,而无需每次查找都进入数据库。

我们还尝试使用 InMemoryCacheManager 作为 FeatureCacheProviderEhCache 的替代品,但结果相同。在这两种实现中,我们确实在我们的 Web 控制台上看到了清除缓存按钮。

另外,我是否有更好的方法来测试我的 api 调用是否实际上是从缓存而不是从数据库中获取数据,而无需关闭数据库?

更新: 在实现我们自己的 FeatureCacheProviderEhCache 并登录之后, 我试图访问 api/ff4j 并且 featureNames 在该响应中是空的。 请参考日志:

METHOD=getCacheFeatures, LINENO=160, MSG=getCacheFeatures :: [ name = ff4jCacheFeatures status = STATUS_ALIVE eternal = false overflowToDisk = true maxEntriesLocalHeap = 10000 maxEntriesLocalDisk = 10000000 memoryStoreEvictionPolicy = LRU timeToLiveSeconds = 120 timeToIdleSeconds = 120 persistence = LOCALTEMPSWAP diskExpiryThreadIntervalSeconds = 120 cacheEventListeners: ; orderedCacheEventListeners:  maxBytesLocalHeap = 0 overflowToOffHeap = false maxBytesLocalOffHeap = 0 maxBytesLocalDisk = 0 pinned = false ]
METHOD=listCachedFeatureNames, LINENO=59, MSG=listCachedFeatureNames[]
METHOD=getCacheFeatures, LINENO=160, MSG=getCacheFeatures :: [ name = ff4jCacheFeatures status = STATUS_ALIVE eternal = false overflowToDisk = true maxEntriesLocalHeap = 10000 maxEntriesLocalDisk = 10000000 memoryStoreEvictionPolicy = LRU timeToLiveSeconds = 120 timeToIdleSeconds = 120 persistence = LOCALTEMPSWAP diskExpiryThreadIntervalSeconds = 120 cacheEventListeners: ; orderedCacheEventListeners:  maxBytesLocalHeap = 0 overflowToOffHeap = false maxBytesLocalOffHeap = 0 maxBytesLocalDisk = 0 pinned = false ]

缓存已创建,但未在其中存储任何值。我正在按照第一个答案中所说的方式设置缓存。 我的日志,当我打印 listCachedFeatures() 时,它也打印为空。 我仍然无法在缓存中看到featureNames。我没有正确配置哪个部分?

【问题讨论】:

    标签: java spring-boot caching offline-caching ff4j


    【解决方案1】:

    首先,我想确认您正确使用了缓存。 ff4j.cache(cacheManager) 是您想要做的。这就是正在发生的事情:

    public FF4j cache(FF4JCacheManager cm) {
        FF4jCacheProxy cp = new FF4jCacheProxy(getFeatureStore(), getPropertiesStore(), cm);
        setFeatureStore(cp);
        setPropertiesStore(cp);
        return this;
    }
    
    1. 当您通过 Web 控制台编辑功能或属性值时,FF4j 知道需要驱逐缓存并立即更新 db,(缓存将仅用于读取)。

    2. FeatureCacheProviderEhCacheInMemoryCacheManager 的工作方式相同。首先使用JSR107。这是个人喜好,也许使用ehcache你可以稍后切换到像Terracotta这样的分布式缓存。

    3. 如何知道只使用缓存而不使用数据库(读取时)?您可以使用 AOP 创建建议来装饰类 FeatureCacheProviderEhCache,或者更基本地在项目中复制 the class,使用适当的日志更新方法并将您的类用作 CacheManager。

    4. 您也可以考虑在微服务级别使用缓存(如果用 Java 实现),而不仅仅是在 Web 控制台中。如果这样做,您可以通过不执行 REST 调用来获取 JSON 来节省一些时间。

    5. EhCache 和 InMemory 缓存就像内存中的名称一样工作,这意味着 2 个微服务在缓存中可能有不同的值。如果您希望微服务之间的缓存一致(非强制性),您希望使用 REDIS 或 HAZELCAST 等分布式缓存。

    【讨论】:

    • 您好,感谢您的回复。我在实现自己的 FF4JCacheManager 后更新了我原来的问题。这是带有额外日志的 FeatureCacheProviderEhCache 的副本。它似乎仍然无法正常工作。有什么帮助吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-30
    • 2021-08-14
    • 2016-01-24
    • 1970-01-01
    • 2014-12-02
    • 2014-05-12
    • 1970-01-01
    相关资源
    最近更新 更多