你的應(yīng)用有開關(guān)嗎?
「啪」天色暗下來時,房間里的燈打開了。
「啪」,睡覺時,房間里的燈又關(guān)上了。
有個開關(guān)可真方便。
「你的應(yīng)用有開關(guān)嗎?為啥不給我加個開關(guān)?」,你的應(yīng)用高叫著,并且在不經(jīng)意的時候,給你來一個突然襲擊。
你的應(yīng)用有開關(guān)嗎?能不能像燈的開關(guān)一樣,在需要時打開,在不需要時關(guān)閉呢,像USB一樣,即插即用呢?
答案當然是一定的,甚至在一些講應(yīng)用開發(fā)、設(shè)計原則的書籍中,都會將應(yīng)用是否飲食「回退」功能做為很重要的一項。這里所謂的回退,和我們所說的開關(guān)類似,都需要在必要的時候?qū)⒐δ芡嘶氐缴壡啊?/p>
假設(shè)在周五下班前,你把這一周新開發(fā)的功能推到線上,開開心心的合上電腦,去健身了。
健身時腦海里還計劃著周末要怎樣happy,想著這些,笑意掛上了嘴角。這時,手機響了。掛斷幾次還堅持打進來。一聽,是接到客戶反饋,線上應(yīng)用出了問題,新開發(fā)的功能影響了其他東西。這該如何時候,只能收拾東西,加班分析新的代碼,找尋修復(fù)的方式,再加新代碼上線,解決這個問題。
如果有「回退」功能,此時你就可以回退到你上線前,然后仔細檢查,不需要急于一時。
如果有開關(guān),你就可以將新的功能「關(guān)」掉,然后線上繼續(xù)跑,不影響其他功能分毫,分分鐘解決問題。
怎么關(guān)呢?
我們都知道 Java 的 class 在類加載器中加載一次,所以如果在線上出現(xiàn)問題需要處理時,就需要停服更新 class 來升級應(yīng)用。雖然像我們之前提到的一些方法,也可以實現(xiàn)熱加載,但生產(chǎn)環(huán)境里較少使用。
除了修改class文件外,我們還可以在代碼里編寫各種 If/else來進行開關(guān)判斷,這個時候如果需要關(guān)閉功能是,停服更新的不再是class,而是配置信息。
我們在應(yīng)用的頁面里大概都寫過類似符合某種條件展示xxx內(nèi)容之類的判斷,例如JSP、FreeMarker 之類的在通過一些條件標簽來進行頁面的渲染進行控制。
在前后端分離,API開發(fā)的時候,如何進行這些返回結(jié)果的控制呢? 這不簡單嘛, if / else 一把梭。
但有些時候,比如需要進行A/B Test, 需要根據(jù)Alpha、Beta 階段進行用戶控制,甚至是線上產(chǎn)生了問題,需要把新上的一個feature停掉... 等等這一系列
問題,如果硬編碼到系統(tǒng)里,每次規(guī)則發(fā)生變更時,都需要修改代碼,部署上線,不夠靈活。
同時,對于A/B Test 這種想要快速實驗的場景,也不夠及時。
為了就對類似上述的場景, Matrin Flower 提出了一個 「Feature Toggle」的概念,對,就是那個提出DI 概念的哥們。(不要吐槽老M 的概念為啥這么多:))。
The basic idea is to have a configuration file that defines a bunch of toggles for various features you have pending. The running application then uses these toggles in order to decide whether or not to show the new feature. |
這里的Toggle就是個開關(guān),針對feature 的開關(guān),決定在什么時候開啟什么feature。
針對 feature,除了可以像上面解決線上臨時問題時進行開關(guān)外,也可以進行訪問權(quán)限控制,對于特定群組的用戶提供某些功能,同時也可以用于實現(xiàn)快速的 A/B Test 的目的,來驗證產(chǎn)品的猜想。
關(guān)于 feature toggle,各種語言有不同的實現(xiàn),具體請參見這里:
http://featureflags.io/resources/
在Java中,較常用的是 Togglz。
Togglz 的使用類似這樣:
- if (MyFeatures.HOT_NEW_FEATURE.isActive()) {
- // 新特性寫這里
- }
你說這不就和我自己寫if/else嗎? 當然不是啦。這個實現(xiàn)將用戶獲取,開關(guān)狀態(tài)獲取都進行了抽象,可以進行自己的Configuration實現(xiàn),
- public class MyTogglzConfiguration implements TogglzConfig {
- public Class<? extends Feature> getFeatureClass() {
- return MyFeatures.class;
- }
- public StateRepository getStateRepository() {
- return new FileBasedStateRepository(new File("/tmp/features.properties"));
- }
- public UserProvider getUserProvider() {
- return new ServletUserProvider();
- }}
然后具體的開關(guān)的狀態(tài)就在stateRepository中進行了定義,可以放在內(nèi)存中,文件中,數(shù)據(jù)庫中等等。此時可以再搭配上配置中心等,來實現(xiàn)應(yīng)用功能的動態(tài)開關(guān),不影響你周末時光。
【本文為51CTO專欄作者“侯樹成”的原創(chuàng)稿件,轉(zhuǎn)載請通過作者微信公眾號『Tomcat那些事兒』獲取授權(quán)】