將某個功能灰度發佈(逐漸放量)給特定線上人羣,避免新功能全量上線帶來的風險。javascript
上面的圖能夠經過兩個方面來理解:css
舉個簡單的例子:將http請求cookie中含有test=1字段的請求都轉發到灰度代碼的機房;
上面經過經過配置特定Nginx規則的方法來達到產品灰度的方法雖然能夠知足必定業務量的需求,可是他也有不少的缺點:html
那麼有沒有更好的方法來作灰度發佈呢?固然是有的,A/B測試就可以彌補上面經過Nginx規則來作灰度的缺點。前端
將線上一部分真實人羣流量隨機拆分紅多個組,對每一個分組的人羣應用不一樣策略或功能,經過計算每組人羣的業務指標(轉化率、成交率等)來衡量策略或功能的實際效果。java
咱們經過下面的這張圖簡單的瞭解下A/B測試的原理:node
由上圖咱們能夠知道A/B和傳統的灰度方法的區別:webpack
傳統的灰度是經過Nginx分發流量到服務器,A/B測試是經過業務代碼區分流量訪問不一樣的代碼塊。nginx
那麼A/B測試的優缺點是什麼呢?git
優勢:web
缺點:
if...else
分支語句。可是這樣還好,由於根據SDK的規範來書寫代碼,仍是很好管理的。前端跟後端很大的區別就是直接面對用戶,就算很簡單的修改一次按鈕的顏色就須要一次上線。這種操做對用戶是可感知的。
現代前端有個特色就是脫離了後端模板引擎的渲染,大多數是使用React、Vue這種MVVM框架的前端(瀏覽器)渲染。這種狀況下後端其實僅僅是給用戶提供一個空的html文件(工做中常常稱做爲殼)。大多數業務代碼開發完之後都是做爲靜態文件上線到服務器,通過用戶訪問後緩存到CDN節點上的。並且這個過程大多數是增量上線的。
其實咱們每次上線完以後服務器上緩存的html文件就包含不一樣的版本信息。若是咱們把這些版本信息管理起來,而且經過特定的手段(對用戶請求應用A/B測試)就能夠完成前端不一樣版本的灰度發佈。
咱們能夠觀察下Webpack或者是其它打包工具打包後的html文件。每次外聯的靜態文件都包含不一樣的hash戳。這些外鏈的文件又都是增量緩存到服務其上的。
index.html (咱們頁面的「殼」)
一些 xxx.js文件 (渲染頁面+頁面的業務邏輯)
xxx.css 文件 (控制頁面顯示樣式)
大概就是下面的這個樣子
基於以上的特色,咱們能不能儘可能減小對業務代碼侵入,而能夠覆蓋業務改動較大的需求進行灰度或者是A/B測試呢?
看下下面的這個這個請求的圖:
每次咱們打包編譯完以後,就將相關的css文件和js文件信息保存到本地的一個json文件中。這些信息的key能夠是咱們的git的tag信息(主要來描述本次發版信息包含的功能等)。
基本上json
文件包含的信息以下:
const version = { // 能夠描述本次的上線內容/ 或者是git tag 'tag1': { 'css': 'xxxxxxx.css', 'app': 'app_xxx.js', 'ventor': 'ver_xxx.js' } }
這裏僅僅是一個簡單的demo示例,可使用Nodejs寫文件的特性直接將文件版本號寫入到index.html返回給前端瀏覽器
Nodejs服務的特色是每次更新完代碼須要重啓以後才能生效。每次上完線重啓服務就會先檢查本地代碼根目錄下的這個json文件。看下這個其中包含的tag是否在DB中存儲,若是有存儲就不作操做,若是沒有就將它存儲DB作持久化。
上面圖上面的Apollo就是用來配置那些用戶訪問新功能的平臺。在Nodejs端,每次接收到用戶請求的時候都會判斷用戶的信息是否知足相關條件,而後從DB中讀取相關靜態文件信息渲染到index.html
中去。
簡單總結下:將每次打包的靜態文件信息先存儲下來,以後請求到達Nodejs的時候判斷用戶是否知足相關條件,若是知足就讀取DB將相關的靜態文件信息返回給Nodejs,Nodejs將靜態頁渲染好以後返回給用戶,達到灰度的目的。
使用Nodejs以前咱們的頁面就是直接部署在服務其上,此次使用了Nodejs後,會有不少其它的問題須要作,好比說Nodejs服務的監控,多機房部署等。這些在大部分的公司應該都有相關的運維工程師來作。我這裏簡單介紹一些其它的內容
這裏的規範包括本地開發時工程目錄的規範和線上用戶訪問url的規範。
在筆者寫這篇文章的時候最新的Nodejs版本已是 11.10
版本了,最新的LTS版本是10.15.1
版本。建議使用Nodejs的同窗都升級本身的Node到8.0
版本以上,由於8.0
版本是一個官方原生支持async...await
語句的版本。
.
├── client // 放置客戶端的代碼
├── index.html
├── index.js
├── node_modules
├── output
├── package-lock.json
├── package.json
├── server // 放置服務端Nodejs代碼
├── test.sh
須要注意的就是在編寫webpack打包工具的時候將server目錄下的給排除掉。放置沒必要要的編譯和產出,增長打包速度。
當使用了新的服務的時候爲了防止跟舊業務的衝突確定須要使用新的url。這個時候就須要作一些約定。目前咱們是這麼約定的
// 域名/產品線/模塊/ http://wwww.aaa.com/driver/bus/index.html // 域名/產品線/模塊/靜態文件目錄 http://wwww.aaa.com/driver/bus/static/js/index.js http://wwww.aaa.com/driver/bus/static/css/index.html
前面提到此次業務升級咱們使用了新的url,可是爲了保證業務的穩定性咱們不是一次性將全部的流量都切到新服務上去的。咱們也是經過批量的切的,因此會存在線上用戶有的地區訪問新服務有地區訪問舊服務。那麼有一天會有所有切換的一天,可是仍是會有一些用戶訪問到舊連接,這個時候能夠經過配置Nginx 的`rewrite
來說舊連接都轉成新的連接。
前端路由能夠分爲兩種方式,hash和path切換。由於對於前端渲染頁面來講,當第一次請求完成後,其實全部的頁面都已經下載到了本地(頁面異步加載除外)。在咱們經過path切換頁面的時候,每次都會向服務端發送請求,其實這些請求是不須要到達Nodejs服務的。咱們能夠經過Nginx配置將這些無用的流量抵擋在Nginx這一層,減小服務器的壓力。
若是是使用hash的方式則不存在這樣的問題,可是會有另外的問題就是對搜索引擎不友好。固然前端路由切換仍是應該根據本身的業務作取捨。
當咱們應用了Nodejs服務以後,能夠拓展的技術點有哪些,一下簡單列舉一些:
固然這種方案也不只僅是可使用Nodejs來作,也可使用其它語言。由於咱們公司已經有基於A/B測試的Nodejs-SDK。我這我就不具體介紹原理了。原理能夠參考百度百科。若是有問題須要一塊兒討論能夠留言或者是郵箱聯繫我:hpuhouzhiqiang@gmail.com