• ff4j 一些高级概念


    feature groups

    通过feature groups 我们可以将同一个release 实例的feature 聚合起来,灵活管理

    • 参考配置
    <?xml version="1.0" encoding="UTF-8" ?>
    <features>
     <!-- Sample Feature Group -->
     <feature-group name="release-2.3">
        <feature uid="users-story1" enable="false" /> 
        <feature uid="users-story2" enable="false" />
     </feature-group>
     <feature uid="featA" enable="true" /> 
     <feature uid="featB" enable="false" />
    </features>
    • 代码访问
    @Test
    public void myGroupTest() {
     FF4j ff4j = new FF4j("ff4j-groups.xml");
     // Check features loaded
     assertEquals(4, ff4j.getFeatures().size());
     assertTrue(ff4j.exist("featA")); 
     assertTrue(ff4j.exist("users-story1"));   
     assertTrue(ff4j.getStore().existGroup("release-2.3")); 
     System.out.println("Features loaded OK");
     // Given 
     assertFalse(ff4j.check("users-story1")); 
     assertFalse(ff4j.check("users-story2"));
     // When 
     ff4j.enableGroup("release-2.3");
     // Then 
     assertTrue(ff4j.check("users-story1")); 
     assertTrue(ff4j.check("users-story2"));
    }
    • 通过FeatureStore操作
    @Test
    public void workWithGroupTest() {
      // Given
      FF4j ff4j = new FF4j("ff4j-groups.xml");
      assertTrue(ff4j.exist("featA"));
      // When 
      ff4j.getStore().addToGroup("featA", "new-group");
      // Then
      assertTrue(ff4j.getStore().existGroup("new-group"));
      assertTrue(ff4j.getStore().readAllGroups().contains("new-group"));
      Map<String, Feature> myGroup = ff4j.getStore().readGroup("new-group");
      assertTrue(myGroup.containsKey("featA"));
      // A feature can be in a single group 
      // Here changing => deleting the last element of a group => deleting the group 
      ff4j.getStore().addToGroup("featA", "group2");
      assertFalse(ff4j.getStore().existGroup("new-group"));
    }

    aop 方式的编程模型

    传统模式,我们都是基于if 以及else 模式check feature ,但是我们可以通过aop 的模式编程,当然aop 的模式也很不错(
    基于规则的开发模式也很不错)
    传统模式

     
    if (ff4j.check("featA")) {
     // new code 
    } else {
     // legacy 
    }
    • aop 模式
      依赖
     
    <dependency>
      <groupId>org.ff4j</groupId>
      <artifactId>ff4j-aop</artifactId>
      <version>${ff4j.version}</version>
    </dependency>

    定义接口注解

    public interface GreetingService {
     @Flip(name="language-french", alterBean="greeting.french")
     String sayHello(String name);
    }
     
     

    定义一个实现

    @Component("greeting.english")
    public class GreetingServiceEnglishImpl implements GreetingService {
     public String sayHello(String name) {
      return "Hello " + name;
     }
    }

    定义另外一个实现

    @Component("greeting.french")
    public class GreetingServiceFrenchImpl implements GreetingService {
      public String sayHello(String name) {
        return "Bonjour " + name;
      }
    }
     

    定义bean

    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <context:component-scan base-package="org.ff4j.aop, org.ff4j.sample"/>
    <bean id="ff4j" class="org.ff4j.FF4j" >
     <property name="store" ref="ff4j.store.inmemory" />
    </bean>
    <bean id="ff4j.store.inmemory" class="org.ff4j.store.InMemoryFeatureStore" >
     <property name="location" value="ff4j-aop.xml" />
    </bean>
    </beans>

    定义feature xml

    <?xml version="1.0" encoding="UTF-8" ?>
      <features>
        <feature uid="language-french" enable="false" />
      </features>

    引用bean

    import junit.framework.Assert;
    import org.ff4j.FF4j; 
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired; 
    import org.springframework.beans.factory.annotation.Qualifier; 
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:*applicationContext-aop.xml")
    public class FeatureFlippingThoughAopTest {
     @Autowired
     private FF4j ff4j;
     @Autowired
     @Qualifier("greeting.english")
     private GreetingService greeting;
     @Test
     public void testAOP() {
      Assert.assertTrue(greeting.sayHello("CLU").startsWith("Hello"));
      ff4j.enable("language-french");
      Assert.assertTrue(greeting.sayHello("CLU").startsWith("Bonjour"));
      } 
    }

    审计以及监控

    前边有介绍过ff4j 对于审计的介绍,我们只需要通过配置就可以,ff4j底层帮助我们处理了

    ff4j.setEventRepository(<HERE YOUR EVENT_REPOSITORY DEFINITION>);
    ff4j.audit(true);

    底层实现

    if (isEnableAudit()) {
     if (fstore != null && !(fstore instanceof FeatureStoreAuditProxy)) {
       this.fstore = new FeatureStoreAuditProxy(this, fstore);
     }
     if (pStore != null && !(pStore instanceof PropertyStoreAuditProxy)) { 
       this.pStore = new PropertyStoreAuditProxy(this, pStore);
     }

    支持的event 存储

    // JDBC
    HikariDataSource hikariDataSource;
    ff4j.setEventRepository(new EventRepositorySpringJdbc(hikariDataSource));
    // ELASTICSEARCH
    URL urlElastic = new URL("http://" + elasticHostName + ":" + elasticPort);
    ElasticConnection connElastic = new ElasticConnection(ElasticConnectionMode.JEST_CLIENT, elasticIndexName, urlElastic);
    ff4j.setEventRepository(new EventRepositoryElastic(connElastic));
    // REDIS
    RedisConnection redisConnection = new RedisConnection(redisHostName, redisPort, redisPassword);
    ff4j.setEventRepository(new EventRepositoryRedis(redisConnection ));
    // MONGODB
    MongoClient mongoClient;
    ff4j.setEventRepository(new EventRepositoryMongo(mongoClient, mongoDatabaseName));
    // CASSANDRA
    Cluster cassandraCluster;
    CassandraConnection cassandraConnection = new CassandraConnection(cassandraCluster)
    ff4j.setEventRepository(new EventRepositoryCassandra(cassandraConnection));

    cache 处理

    cache 对于大量数据访问是一个不错的选择,ff4j 提供了好多cache 的支持
    参考内置实现

     
    // REDIS (dependency: ff4j-store-redis)
    RedisConnection redisConnection = new RedisConnection(redisHostName, redisPort, redisPassword);
    FF4JCacheManager ff4jCache = new FF4jCacheManagerRedis(redisConnection );
    // EHCACHE (dependency: ff4j-store-ehcache)
    FF4JCacheManager ff4jCache = new FeatureCacheProviderEhCache();
    // HAZELCAST (dependency: ff4j-store-hazelcast)
    Config hazelcastConfig;
    Properties systemProperties;
    FF4JCacheManager ff4jCache = new CacheManagerHazelCast(hazelcastConfig, systemProperties);
    // JHIPSTER
    HazelcastInstance hazelcastInstance;
    FF4JCacheManager ff4jCache = new JHipsterHazelcastCacheManager(hazelcastInstance);
     

    配置ff4j 实例

    ff4j.cache(ff4jCache);

    spring boot 集成

    当然官方提供了spring boot 的starter,使用起来也是比较简单的
    添加依赖

     
    <dependency>
        <groupId>org.ff4j</groupId>
        <artifactId>ff4j-jmx</artifactId>
    </dependency>

    引用


    @Configuration
    public class FF4JConfiguration {
        /**
         * Create and configure FF4J
         */
        @Bean
        public FF4j getFF4j() {
            return new FF4j("ff4j.xml");
        }
    }

    参考资料

    https://github.com/ff4j/ff4j/wiki/Advanced-Concepts

  • 相关阅读:
    php-fpm: 某项目网站频繁出现503问题解决( WARNING: [pool www] server reached pm.max_children setting (50), consider raising it)
    spring mvc: rss(xml)输出
    spring mvc: json练习
    spring mvc: xml练习
    spring mvc:输出json,输出多个json
    phalcon: 目录分组后的acl权限控制
    spring mvc: xml生成
    spring mvc:视图解析器
    Python爬虫从入门到放弃(二十)之 Scrapy分布式原理
    Python爬虫从入门到放弃(十九)之 Scrapy爬取所有知乎用户信息(下)
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/12739853.html
Copyright © 2020-2023  润新知