最近一個使用Spring的項目中須要進行性能調優。方式基本上是編寫新的代碼實現原來同樣的業務邏輯,只是實現方式有一些調整,例如增長cache,優化算法等等。html
一開始你們但願直接在原有代碼基礎上修改,可是這樣一來,就要跟上每週一次的發佈節奏,一週搞定難度太大。因而決定拷貝出的package來重構。在沒啓用以前這個package下都是dead code。這樣作的好處有幾點:java
既然要實現上述第三點,也就是利用配置來實現切換,那麼這個Enable的flag就不該該寫到代碼裏,甚至是配置文件裏,由於項目啓動都是在docker中經過spring-boot的cmd直接啓動的。DevOps是不容許進入docker進行操做的。nginx
想到咱們的整個部署架構是基於Kubernetes的,能夠經過修改工程的deployment.yaml文件來實現。原理就是deployment裏面設置一個docker的Env,Key是FeatureToggle
,Value能夠是這樣FeatureA,FeatureB
,當docker啓動時,JVM(Java代碼)能夠經過System.getenv()
來得到環境變量,歷來知道這個Feature是須要啓用仍是不啓用。如上的寫法表示FeatureA和FeatureB是啓用的。算法
咱們能夠寫一個簡單的接口實現來判斷:spring
public boolean isFeatureEnable(String featureName) { // 用System.getenv("FeatureToggle")讀取環境變量判斷是否包含參數的featureName // ... }
雖然咱們有了判斷方法,可是由於項目組的人都有潔癖,咱們不但願代碼中處處都是docker
if(isFeatureEnable(featureA)) { // new code } else { // old code }
這樣實在是太ugly了。api
咱們須要利用spring的IoC特性來切換implementations。Spring從4.0開始提供Conditional的註解。結合@Configuration
就能夠實現app啓動時的不一樣Bean的注入。架構
寫一個FeatureA的Condition Classapp
public class FeatureACondition implements Condition{ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return isFeatureEnable("featureA") } }
再寫一個Spring的Configuration來使用這個Conditionide
@Configuration @Conditional(FeatureACondition.class) public class FeatureAConfiguration { @Bean(name="bizService") public BizService bizService(){ return new EnhancedBizService(); } }
固然若是要實現互斥的切換,即啓用FeatureA另外一個Bean就不能加載的話,那麼再寫一個NotFeatureA的Configuration就能夠了。
@Configuration @Conditional(NotFeatureACondition.class) public class NotFeatureAConfiguration { @Bean(name="bizService") public BizService bizService(){ return new OldBizService(); } }
這樣一來,當FeatureA啓用時BizService這個interface的實現就是EnhancedBizService,反之它的實現就是OldBizService。
固然你在configuration上用@ComponentScan
,@Import
等等都是沒問題的,在啓動時都會最早判斷Conditional,若是不知足spring根本不會繼續下面的掃描或者加載操做。
最後啓用這兩個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。
對於Devops而言,只須要在deployment裏面修改Env的內容,再重啓deploy這個app就能夠實現Feature Toggle了。即便你不使用Kubernetes,docker-compose也是同樣的道理。
經過修改docker-compose.yml
實現:
environment: - FeatureToggle=FeatureA,FeatureB - SESSION_SECRET
總而言之就是充分利用OO語言的優點,實現可拔插的FeatureToggle。接下來咱們還會繼續研究如何Runtime的啓用Feature,我也發現了一個已有的輪子togglz。若是有朋友用過,歡迎反饋使用感覺。