• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • 使用功能開關更好地實現持續部署

    發表于:2013-12-04來源:InfoQ作者:崔力強點擊數: 標簽:部署
    使用功能開關更好地實現持續部署。為了快速發布開發完成的功能,現代的互聯網企業通常會以比較快的迭代周期來持續的發布。但是有時候因為技術或者業務上的原因,需要在發布的時候將某些功能隱藏起來。一種解決方案是,在獨立的分支上開發新功能,全部開發測試完成之后,

      摘要

      為了快速發布開發完成的功能,現代的互聯網企業通常會以比較快的迭代周期來持續的發布。但是有時候因為技術或者業務上的原因,需要在發布的時候將某些功能隱藏起來。一種解決方案是,在獨立的分支上開發新功能,全部開發測試完成之后,才合并回主干,準備發布。這也就是我們經常提到的功能分支(feature branch)。本文將介紹如何使用功能開關(feature toggle)來更好地解決這個問題,及其在一個典型Spring web應用程序中的具體實現,最后討論了功能開關和持續集成如何協同工作。

      功能分支的問題

      功能分支可以幫助我們同時開發多個新功能,而不對發布的節奏造成影響,這解決我們一開始提到的那個持續發布的需求,但是它也會引入很多問題。在Martin Fowler的文章中已經很全面的闡述了這些問題,簡單總結如下:

      分支分出去時間長了往主干合并的時候會出現很多的代碼沖突。

      在一個分支中修改了函數名字,但是如果在其它分支中大量使用修改前的函數名,則會引入大量編譯錯誤。這點被稱為語義沖突(semantic conflict)

      為了減少語義沖突,會盡量少做重構。而重構是持續改進代碼質量的手段。如果在開發的過程中持續不斷的存在功能分支,就會阻礙代碼質量的改進。

      一旦代碼庫中存在了分支,也就不再是真正的持續集成了。當然你可以給每個分支建立一個對應的CI,但它只能測試當前分支的正確性。如果在一個分支中修改了函數功能,但是在另一個分支還是按照原來的假設在使用,在合并的時候會引入bug,需要大量的時間來修復這些bug。

      功能開關

      第一原則,代碼庫中不再引入任何分支,所有的代碼都提交到同一個主線(mainline),在開始開發一個新功能的時候,引入一個布爾值的配置項,使得在該配置項為假時,應用程序的外部行為和沒有引入該功能之前保持一致;而在配置項為真時,應用程序才展現出那些新開發的功能。

      實現的方式也很直觀。在所有跟該功能相關的代碼中都會讀取該配置項的值,如果配置項值為真,則使用新功能,如果為假,則保持以前的邏輯。我們把在某處代碼使用到該布爾配置項稱為該處代碼使用了該開關。

      對于一個典型的Spring的web項目,代碼庫中會包括Java代碼、JSP代碼,IOC配置文件,還有CSS和JS文件。這些都是代碼,根據不同的業務需求,這些代碼都有可能會用到開關。為了能夠在這些代碼中方便地獲取開關的值,使用開關,我們需要一些基礎設施來支持。

      如上圖所示。需要在“功能開關”的模塊中實現所需要的基礎設施,然后配合配置文件的內容來對應用程序的行為進行控制。下面我們就配置文件和基礎設施做一些討論。

      功能開關配置文件

      Spring中使用MessageSource來實現國際化,其本質上就是從一系列的properties文件中讀取鍵值對。我們這里使用這些properties文件來存儲功能開關的配置項,如這樣的項:

      featureA.isActivated=true

      在MessageSource之上我們封裝了一層ApplicationConfig,用來提供便利的方法(如getMessageAsBoolean等)來獲取配置項的值。

      功能開關基礎設施

      為了在代碼中使用到功能開關配置文件的內容。我們需要實現一些基礎設施。

      Java代碼中

      將ApplicationConfig的實例bean注入到需要應用開關的其他bean中,然后在其它bean中讀取相關配置項。這種注入可以很容易的使用Spring來完成。

      JSP中

      自定義一個JSP Tag來在JSP中使用配置文件中的配置項,其使用方法如下:

      在調用過該tag之后,就可以使用featureValue這個變量來引用對應配置項的值了。

      IOC配置文件中

      在Spring的IOC配置文件中,同樣可以使用自定義的Tag來動態選取bean的實例。其原理如下圖所示:

      類A依賴于B接口,bean1和bean2是在Spring配置文件中定義好的兩個實例bean,他們的類型都是B接口的實現類,因此他們都可以被注入到A的實例bean中。通過開關的控制,可以把不同的實例注入到類A的實例bean中。

      關于CSS和JS,我們并沒有再引入更多的基礎設施,通過JSP中的控制就可以完成對CSS/JS的控制。

      例子一

      問題:開發了一個新的功能,而該功能需要通過主頁上的一個鏈接訪問。

      利用上述的基礎設施,可以這么實現:

      在資源文件中定義該功能開關的狀態。

      //feature-config.properties

      show.link.feature=true

      在JSP中使用自定義的ns:config Tag來讀取配置項的值,根據該值決定是否顯示鏈接。

      //index.jsp

      link to new function

      在Controller代碼中讀取開關的值,如果開關狀態為關閉,則在訪問該功能時直接返回404。

      //NewFunctionController.java

      ......

      protected ModelAndView handle(HttpServletRequest request, HttpServletResponse

      response, Object command, BindException bindingResult) throws Exception {

      if(!applicationConfig.getMessageAsBoolean("show.link.feature")) {

      return new ModelAndView("404.jsp");

      }

      //normal logic

      }

      ......

      例子二

      問題:我們的產品已經在使用google map API V2的服務,現在要升級到V3。

      首先還是要引入一個功能配置項:feature.googleV3Service.isActivated。

      google map API V2相關的邏輯全部存在于一個具體類型GoogleMapV2Service中。而SearchLocationService直接依賴于GoogleMapV2Service這個具體類型,現在為了方便替換,引入一個接口作為抽象層。

    原文轉自:http://www.kjueaiud.com/deltestingadmindd/

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>