利用Spring的Conditional注解來實現FeatureToggle

TiffinyPEJJ 8年前發布 | 31K 次閱讀 Spring Docker JEE框架

最近一個使用Spring的項目中需要進行性能調優。方式基本上是編寫新的代碼實現原來一樣的業務邏輯,只是實現方式有一些調整,例如增加cache,優化算法等等。

一開始大家希望直接在原有代碼基礎上修改,但是這樣一來,就要跟上每周一次的發布節奏,一周搞定難度太大。于是決定拷貝出的package來重構。在沒啟用之前這個package下都是dead code。這樣做的好處有幾點:

  • 在調優后的code啟用前,業務至少不會受影響。
  • 利用docker的特性,可以實現灰度發布,比如啟動兩個docker,一個是老的code,一個啟用新的code,利用nginx實現分流。
  • 灰度發布后發現有緊急bug,只需要devOps修改一點配置,重啟docker可以再切回老的code。

出發點

既然要實現上述第三點,也就是利用配置來實現切換,那么這個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是啟用的。

我們可以寫一個簡單的接口實現來判斷:

publicbooleanisFeatureEnable(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

publicclassFeatureAConditionimplementsCondition{

@Override
publicbooleanmatches(ConditionContext context, AnnotatedTypeMetadata metadata){
returnisFeatureEnable("featureA")
 }
}

再寫一個Spring的Configuration來使用這個Condition

@Configuration
@Conditional(FeatureACondition.class)
publicclassFeatureAConfiguration{

@Bean(name="bizService")
publicBizServicebizService(){
returnnewEnhancedBizService();
 }

}

當然如果要實現互斥的切換,即啟用FeatureA另一個Bean就不能加載的話,那么再寫一個NotFeatureA的Configuration就可以了。

@Configuration
@Conditional(NotFeatureACondition.class)
publicclassNotFeatureAConfiguration{

@Bean(name="bizService")
publicBizServicebizService(){
returnnewOldBizService();
 }

}

這樣一來,當FeatureA啟用時BizService這個interface的實現就是EnhancedBizService,反之它的實現就是OldBizService。

當然你在configuration上用 @ComponentScan , @Import 等等都是沒問題的,在啟動時都會最先判斷Conditional,如果不滿足spring根本不會繼續下面的掃描或者加載操作。

最后啟用這兩個Config

在項目啟動入口

@SpringBootApplication
@Import({NotFeatureAConfiguration.class, FeatureAConfiguration.class})
publicclassApplication{

publicstaticvoidmain(String[] args){
 SpringApplication.run(Application.class, args);
 }

}

小結

通過上述幾步,在spring項目啟動時通過conditional注解的條件判斷,實現不同Bean的裝配,從而啟用不同的Feature。

對于Devops而言,只需要在deployment里面修改Env的內容,再重啟deploy這個app就可以實現Feature Toggle了。即使你不使用Kubernetes,docker-compose也是一樣的道理。

通過修改 docker-compose.yml 實現:

environment:
 - FeatureToggle=FeatureA,FeatureB
 - SESSION_SECRET

總而言之就是充分利用OO語言的優勢,實現可拔插的FeatureToggle。接下來我們還會繼續研究如何Runtime的啟用Feature,我也發現了一個已有的輪子 togglz 。

 

來自:http://www.deanwangpro.com/2016/10/30/spring-featuretoggle/

 

 本文由用戶 TiffinyPEJJ 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!