Golang 跟 PHP 或 Nodejs 不一樣,是一種需要預先 compile 的語言,相信這個大家都知道了。然而,這有一個很大的問題,就是無法像 php 那樣動態載入插件 / 模組。
那如果我想在已經 Compile 好的 Golang 程式上加上額外的功能應該怎樣做?
一開始我就為這問題煩惱了很久,然而最後我發現了一個不錯的解決方法,就是使用 Reverse Proxy。舉個例子,如果你能夠設定動態的 Reverse Proxy ,然後把一個新的模組指向一個新的 subdirectory ,那樣看上去不就跟新增了功能很相近了嗎?
所以,就在新的 ArOZ Online Core 裡面加入了這樣的一個結構,這裡我把它稱為 Subservice:
Subservice 的概念很簡單,就是把 Reverse Proxy 的 process 拼進去跟 Core 共用同一個 STDIN 跟 STDOUT,然後網頁的部分就是用 Reverse Proxy 來處理,看上去所有模組都是由同一個程式所執行似的(實際上卻不是),它的運作邏輯大約是這樣:
- 掃描所有存在的資料夾,看看裡面有沒有可執行檔案
- 使用 cmd.exec 加上 -info argument 啟動,等待回傳 JSON 格式的啟動設定
- 把 working directory 轉換到資料夾內,並以 cmd.exec -port {指派的端口} 執行該程式
- 把程式的 STDERR 跟 STDOUT 都指向 parent 的 STDOUT
- 把服務要求的 Reverse Proxy 終端指向到該程式
- 在用家存取該終端的時候進行 URL rewrite
- 【在 Reverse Proxy 下使用模組】
- 在parent 遇上 SIGKILL 的時候把 SIGKILL 都傳送到子服務並關掉程式
問題:可是這樣就存取不了伺服器的 DB 跟 File System API
對,所以這方法後來改成了只讓非兼容 ArOZ Online 的模組使用(例如 Aria2)。對於真的有需要存取系統核心部分 API 的模組來說,這個方法無法處理到 Database 存取跟 File System Virtualization 的部分,如果把 系統核心 API 都用 RESTful 的方式開出來錄又會有另一些安全問題,所以得想另一個方法去處理這個部分。
欸,那我內嵌一套 JavaScript Interpreter 不就可以了嗎?
沒錯,所以後來就使用了一個新的架構,暫名 AJGI。這是一個可以讓系統執行 Javascript 的方法,同時讓 JavaScript 存取到系統內部的 function,可謂一舉兩得,至於效率嘛,既然你都是在寫插件就不要介意這麼多了。
簡單來說邏輯是這樣的
- 由 front end 呼叫 AJGI ,並從檔案系統載入一段兼容的 JavaScript 代碼
- 在 JavaScript 虛擬機中執行代碼
- 透過 轉接器 使用 JavaScript 存取 ArOZ Online Core Golang 部分的核心功能(如需要)
- 回傳資料到 frontend
- 結束虛擬機
至於功能和限制之類的就要等之後的開發再進行測試才知道了