從 PHP 轉到 Node.js 的那些事

未濟發表於2016-06-08

如果你們開發團隊正在使用PHP,並考慮遷移到Node.js,這篇文章很適合你。本文並不探討從PHP移植到Node.js的細節,以及Node.js的基礎知識。而是涵蓋:決策制定、著手點的描述、編寫 Node.js 伺服器的深層次注意事項、以及部署策略。

為什麼遷移?

1stdibs 決定從 Apache/PHP 遷移到 Node.js+Express 有五個理由:

  1. 程式碼更少
  2. 全棧式JS
  3. 開發人員幸福度更高
  4. 投入回報率
  5. 未來的優化

程式碼更少

1stdibs基於面向服務體系架構(SAO),前端呼叫後臺的JAVA服務。這意味著需要同時維護前端模型,以及服務端PHP和客戶端JS模板。試想一下,如果可以擺脫PHP,就能夠統一前端展現與後臺模型於一種語言:JavaScript(同時可以合併一些模板)。從維護的角度來看,這麼做程式碼更簡潔,並且沒有重複邏輯。

同構JavaScript萬歲!

全棧式JS(及其優點)

整個開發棧使用一種語言很簡便。對開發者來說,較少的環境切換使他們開心和高效。額外的好處是工具使用更簡單。相比之前使用Composernpm兩個包管理器,現在只需要一個。儘管Composer很出色,由於nbp負責工具和客戶端管理,nbp總是必要的。一旦去掉所有的PHP程式碼,nbp將成為僅有的包管理器。

開發人員樂意

我們要保證開發人員的技能集得到擴充套件、職業生涯不斷髮展,這一點很重要。對於JavaScript工程師而言,Node.js極具吸引力。能夠在服務端使用與客戶端相同的工具、風格和模式,是非常順手和高效的。此外,Node.js相當流行,在企業級開發上也得到了長足發展。Node.js是JavaScript工程師的必備技能。

投入回報率

我們在招聘優秀的JS工程師和培訓初級JS工程師方面花了大價錢。由於客戶端棧很複雜,我們需要高階JavaScript工程師。我們不再僱用PHP工程師,僅僅僱用了JavaScript工程師。我們的觀點是,為什麼不培養他們在服務端的技能呢?

未來的優化

長遠而言,我們打算把兩個龐大的應用分割成一系列獨立部署的小應用。這很容易通過Node.js、Express和nbp實現。理論上,PHP(比如使用Slim)可以做同樣的事。但我們非但得不到上述好處,還會搞得一團糟:在Apache/PHP上進行操作會更加複雜,基礎設施也會變得有些奇怪。

選擇框架

那個最終被我們用Node.js替換掉的PHP應用,主要有如下職責:

  1. 登入和授權
  2. 路由選擇和服務端模板引擎(服務HTML)
  3. 引導前端應用
  4. 代理服務(為了迴避CORS)
  5. 服務靜態資源(js,css,images)

這些就是我們需要替換掉的基本功能。

我們嘗試過不少框架,Express令人歎為觀止(試一下我們評估過的spreadsheet)。任何未基於Express 的框架看起來都不靠譜。Express通俗易懂,並有良好的文件。另外,可以招聘到正經培訓過Express的人。

我們新增了一些kraken的核心模組(express-enrouten用於路由選擇、lusca負責安全);此外,i18n-node提供國際化支援,模板引擎使用Swig(我們後來放棄了Swig。呵呵,開源軟體還是有風險的)。

我們考慮過全盤使用kraken,但是從原來的服務端php模板引擎Twig切換到Swig直截了當,還很快捷。此外,kraken裡面的Dust和i18n也不討人喜歡。

編寫伺服器

選好了框架,下一步該寫伺服器了。

使用Apache+PHP時,你不需要再寫一個伺服器。Apache本身就是伺服器,PHP是應用。如果使用Node.js,伺服器和應用是同一個。從Apache/PHP轉到PHP,你需要處理一些之前自然而然使用的功能,這一點很重要。使用Apache,你(或者系統管理員)配置伺服器,在PHP應用裡完全不用關心Apache為你處理的那些事。Node.js卻以一種不同的方式來工作。

提供靜態檔案服務

毋庸置疑,提供靜態檔案服務是Apache的核心功能。Node.js與此不同,你要在應用中配置靜態檔案服務。幸運的是這很簡單,有良好的文件說明,並且是在Express中實現的

日誌

很多基本的Apache配置為你提供訪問日誌和錯誤日誌。使用Node.js時,估計你也猜到了,同樣需要在應用中配置。所幸很多優秀的開源軟體包使之變得很簡單。Morgan是一個基本的請求日誌記錄器,它配置簡單,允許你把日誌寫到輸出流(標準輸出裝置或檔案)。如果你需要把日誌寫到資料庫,或者有別的(更高階的)日誌需求,那就試一下winston吧。

代理

我們有一個基本需求:能夠代理傳輸客戶端ajax請求到後臺服務。相比於處理CORS頭,代理所有來自相同域的請求要簡單得多。但你要想通過代理使用webpack-dev-server(正如我們所做的),就必須在Node.js應用中處理這一問題。http-proxy是一個簡單可靠的解決方案。

剩下的工作

除了上面提到的,還有一些列別的工作需要完成。我們從一個MVC應用談起,該應用基於 CodeIgniter(CI)框架,為一系列單頁應用提供服務。大部分工作就是移植:

  • CI控制器移植到Express路由選擇器和中介軟體(包括登入和認證)
  • Twig模板引擎移植到Swig(這一步比較瑣碎)
  • Service層資料訪問(以便正常啟動客戶端單頁應用)

上面並未列出CodeIgniter模型這一關鍵元件。事實上根本不用重寫PHP模型!太給力了!我們的客戶端應用使用Backbone模型。當然這要擴充套件Backbone.Model.sync,從而使之全域性地工作在伺服器和客戶端。

部署

如果你的app規模較大,不應該一次性全部上線。可以通過漸進式部署的方式逐步上線。我們因此花費了好幾周。

漸進式部署的優點:

  • 最小化bug範圍
  • 每次在釋出一部分路由及功能的同時,其他工程師可以正常進行開發
  • 對正在進行的功能開發和改進影響最小 — 新功能可以繼續釋出(這可能導致重複的工作)
  • 如果操作得當,可以快速回滾到之前的服務

NGINX很不錯

該如何逐步上線呢?我們在眾多的伺服器中挑選了Nginx

Nginx允許你一次只“開啟”一個路由(如果發現情況不妙就關掉 — 正如我們多次遇到的),這給了你很大的自由度。我們也發現開啟路由的時候不用部署程式碼,這很有幫助。這在一週一次的釋出計劃裡,為我們提供了一些迴旋空間。

不過有一個缺點,你需要確保客戶端程式碼同時接受舊的Apache/PHP伺服器和新的Node.js伺服器提供的服務。這並不可怕,不過你要把舊伺服器上未優化的功能移植到新的伺服器。屏住呼吸去做吧(記得寫一個便利貼去清理你的技術債)。

總結

從頭到尾,整個移植工作大概花費了一年。這聽起來可能有點荒謬,不過這個時間表包括決策過程(比較匆忙)、基於Express寫一個滿足需求的核心框架、移植所有功能、逐步漸進式上線。此外,請記住,我們始終只有一兩個開發者為之工作 — 並且是兼職。

如果你想嘗試一下,請慎重考慮。你的團隊能否受益?你的整個組織能否受益?如果你來自商業組織,請記住商業需要持續運轉。你需要在商業目標和工程目標之間找到良好的平衡。

我們很高興嘗試過。

相關文章