SpringのConditional注記によるFeatureToggleの実装


Springを使用した最近のプロジェクトでは、パフォーマンスのチューニングが必要です.方式は基本的に新しいコードを作成して元の同じビジネスロジックを実現しますが、実現方式にはcacheを増やしたり、アルゴリズムを最適化したりするなどの調整があります.
最初は既存のコードを直接修正したいと思っていましたが、そうすると、週に1回のリリースペースに追いつき、1週間で解決するのは難しいです.そこでコピーしたパッケージを再構築することにした.有効になる前にこのパッケージの下はdead codeです.このようなメリットはいくつかあります.
  • は、チューニング後のcodeが有効になるまで、少なくともビジネスに影響を及ぼさない.
  • はdockerの特性を利用して、階調の発表を実現することができて、例えば2つのdockerを起動することができて、1つは古いcodeで、1つは新しいcodeを有効にして、nginxを利用して分流を実現します.
  • 階調が発表された後、緊急バグが発見され、devOpsが少し構成を変更するだけで、dockerを再起動して古いcodeを切ることができます.

  • 出発点
    上記の第3の点、すなわち構成を利用して切り替えを実現する以上、このEnableのflagはコードに書くべきではなく、プロファイルに書くべきではありません.プロジェクトの起動はdockerの中でspring-bootのcmdを通じて直接起動されるからです.DevOpsはdockerに入って操作することは許されません.
    インプリメンテーション
    デルの導入アーキテクチャ全体がKubernetesに基づいていることを考えると、エンジニアリングのdeploymentを変更することができます.yamlファイルで実現します.原理はdeploymentにdockerのEnvを設定することであり、KeyはFeatureToggleであり、ValueはこのようにFeatureA,FeatureBであってもよく、dockerが起動するとJVM(Javaコード)はSystem.getenv()で環境変数を得ることができ、このFeatureが有効にする必要があるか無効にする必要があるかはこれまで知られていなかった.上記のようにFeatureAとFeatureBが有効になっていることを示す.
    簡単なインタフェース実装を書いて判断することができます.
    public boolean isFeatureEnable(String featureName) {
        //  System.getenv("FeatureToggle")               featureName
        // ...
    }

    さらに使用
    判断方法はありますが、プロジェクトチームの人は潔癖なので、コードのあちこちに
    if(isFeatureEnable(featureA)) {
        // new code
    } else {
        // old code
    }

    これは本当にuglyですね.
    springのIoC特性を利用してimplementationsを切り替える必要がある.Springは4.0からConditionalの注釈を提供する.@Configurationと組み合わせて、app起動時の異なるBeanの注入を実現することができる.
    FeatureAのCondition Classを書く
    public class FeatureACondition implements Condition{
     
      @Override
      public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return isFeatureEnable("featureA")
      }
    }

    SpringのConfigurationをもう一つ書いてこのConditionを使います
    @Configuration
    @Conditional(FeatureACondition.class)
    public class FeatureAConfiguration {
     
      @Bean(name="bizService")
      public BizService bizService(){
          return new EnhancedBizService();
      }
     
    }

    もちろん、反発の切り替え、すなわちFeatureAを有効にして別のBeanをロードできない場合は、NotFeatureAのコンフィギュレーションをもう1つ書けばよい.
    @Configuration
    @Conditional(NotFeatureACondition.class)
    public class NotFeatureAConfiguration {
     
      @Bean(name="bizService")
      public BizService bizService(){
          return new OldBizService();
      }
     
    }

    このようにFeatureAが有効になっている場合、BizServiceというインターフェースの実装はEnhancedBizServiceであり、逆にその実装はOldBizServiceである.もちろん、configurationで@ComponentScan@Importなどを使うのは大丈夫ですが、起動時にはまずConditionalを判断し、springを満たさなければ次のスキャンやロード操作を継続しません.
    最後にこの2つのConfigを有効にしてプロジェクトの開始ポートに
    @SpringBootApplication
    @Import({NotFeatureAConfiguration.class, FeatureAConfiguration.class})
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
    }

    小結
    上記の手順により、springプロジェクトの開始時にconditional注記の条件判断により、異なるBeanのアセンブリが実現され、異なるFeatureが有効になります.Deopsにとって、deploymentでEnvの内容を修正し、deployというappを再起動するだけでFeature Toggleが実現します.Kubernetesを使わなくてもdocker-composeは同じ理屈です.docker-compose.ymlを修正することによって実現される.
    environment:
      - FeatureToggle=FeatureA,FeatureB
      - SESSION_SECRET

    要するにOO言語の優位性を十分に利用し、抜き差し可能なFeatureToggleを実現することである.次にRuntimeのFeatureの有効化についても引き続き検討し,既存のホイールtogglzも発見した.友达が使ったことがあるなら、フィードバックを歓迎します.