從今年年初開始,我就嘗試在業餘時間和一個朋友開發一個容器平臺,更多地是實驗一些新的技術,也但願可以經過它將本身的一些小應用管理起來,在基本完成後可能會考慮開源。之因此說是實驗是由於我選擇了一個我幾乎徹底不瞭解的技術棧:主要編程語言是 Golang、只使用 Etcd 做爲數據庫、基於 Docker Swarm 管理容器。前端
不得不說 Golang 是一個很是難用的語言,在語言層面,爲了所謂的「簡單」而沒有添加 異常 和 泛型 這兩個對於高級編程很是重要的特性;在生態上仍沒有統一出一個包管理器,若是隻發佈編譯好的二進制程序卻是沒問題,但若是發佈源代碼的話,缺乏統一的包管理會帶來不少麻煩,以致於不少開發者選擇將 vendor 直接包含在版本控制中。node
在這個項目中,沒有異常和泛型真的給我帶來了很大的困擾,幾乎一半的代碼都在進行繁瑣的錯誤檢查,沒有泛型則很難實現一些通用的函數,或者不得不進行強制類型轉換。這讓我以爲 Golang 的使用場景很是受限:由於有 GC,它難以勝任對實時性要求較高的底層的工做;又由於缺乏高層次的抽象手段,不適合業務邏輯複雜的應用編程(例如 Web 後端),能夠說不上不下,只適合於一些業務邏輯不復雜的中間件,或者一些客戶端命令行工具(畢竟在三個平臺下都沒有運行時依賴)。git
Etcd 是一個我以前沒有接觸過的數據庫類型,它是分佈式的鍵值數據庫,能夠在大多數節點存活的狀況下保證讀寫的強一致性,也提供了事務、訂閱修改、TTL、檢索歷史快照等功能。我在這個項目中直接使用 Etcd 做爲惟一的數據庫存儲全部數據,也使用 Golang 對 Etcd 的 API 進行了簡單的封裝,以便更好地使用 JSON 和 Etcd 的事務。github
由於畢竟是業餘項目,這個項目一直進展緩慢,在今年的最後我還嘗試在 Swarm 上實現高可用的有狀態容器,例如 Redis 和 MongoDB。我在容器內用 Shell 編寫了一系列的腳本,在啓動時從 Etcd 獲取集羣信息和本身的角色,而後經過長輪詢完成配置的切換,再運行一個 Nginx 將從節點的流量轉發給主節點,容器的數量則由 Swarm 保證,實現了一個「自維護」的數據庫容器。數據庫
在去年 Node.js 錯誤處理實踐 的基礎上,今年我又在繼續探索錯誤處理和日誌的最佳實踐。以前的方法存在一個問題,即我特別關注於將錯誤對象原樣地傳遞出去,但有時看到一個很是底層、很是細節的錯誤(例如 CONNTIMEOUT),則難以判斷究竟發生了什麼。雖然從異常的調用棧中能夠看出調用路徑,但並不能看到一些關鍵變量的值,例如這個鏈接錯誤是在請求哪一個地址,主要參數是什麼,這是由於在異常傳遞的過程當中,咱們並無記錄這個信息。最後只能獲得一個很是細節的錯誤信息,而不知道這個錯誤發生在更上層的哪一個環節。編程
因而我開始使用 verror 這個庫,它最主要的功能是幫助你建立一個「異常鏈」,你能夠在每一個層級來向異常上補充路徑信息(會被反映到 err.message
例如一個來自底層的錯誤信息多是 request failed: failed to stat "/junk": No such file or directory
這樣)。這個異常鏈信息也會和其餘元信息一塊兒以結構化的方式存儲在錯誤對象上,這個庫也提供了一些工具函數來獲取這些結構化信息。我嘗試使用 verror 來管理全部的異常,報告帶有詳細的、每一層級信息的異常。同時我也會向錯誤對象上附加一些元信息用來指示如何響應客戶端、是否須要發到 Sentry、是否能夠重試等。小程序
除了異常,我也開始嘗試使用 bunyan 打印結構化的日誌,並存儲到 Elasticsearch。經過 Kibana 的 Web UI 能夠很簡單地對日誌進行篩選和查詢,在排查問題時找到相關的那部分日誌。對於一個既有的系統來講,調整異常和日誌能夠說是一個很是龐雜的工做,在調整的過程當中也我也在不斷地修正本身的實踐,今年一全年我都在作這樣的嘗試。後端
對於一個稍微複雜一點的項目來講,並非全部的數據都在事務的保護下 —— 其實不少互聯網項目也並不會使用事務。這樣就不免出現數據不一致的狀況,這種不一致多是數據的關係出現損壞、緩存和數據不一致,也多是多種數據庫甚至外部資源的狀態沒有同步。微信小程序
今年我探索瞭解決這個問題的一種實踐:編寫腳本去自動地檢查和恢復這種不一致,這種腳本是常態化運行的,例如個人一個項目中如今有 4 個腳本以每 10 分鐘左右的頻率在進行各類檢查和恢復。這樣不一致的數據會在很短的時間內被恢復(也會留下可查的記錄),對於用戶來講就是碰到問題的次數變少了,在一些重大的的故障發生時,這種腳本也能夠幫助你快速地恢復服務。緩存
這樣自動地修復不一致也引入了一個問題:就是在覈心業務中會不自覺地下降對一致性的追求 —— 反正有腳原本修復,問題不會暴露出來。目前只能是爲檢查和恢復的狀況繪製圖表,在不一致的頻率超出預期時及時地發現。
由於雲引擎的 負載均衡 邏輯比較複雜,以前是在一個開源的 Node.js 反向代理組件上進行了一些二次開發,但在高峯時的性能不是很理想,一直有想法換成 Nginx。因而今年年初我就開始基於 Openresty 用 Lua 重寫了負載均衡組件,效果很是理想,只用了 Node.js 十分之一的 CPU 和內存,再也沒有出現容量不足的狀況。
緣由固然是 Nginx 對內存有着很是細粒度的管理,只在請求開始和結束時申請和釋放整塊內存,也沒有 GC,保持一個長連接幾乎不須要消耗多少資源。Openresty 則將 Lua 嵌入到了 Nginx 中,在 Nginx 高性能的請求處理和豐富的 HTTP 功能的基礎上,讓你能夠用 Lua 去實現一些邏輯,對於負載均衡確定是夠用了。
我以前一直有在使用 pass 這個基於 GPG 和 Git 的命令行密碼管理器,並將密碼倉庫託管在 GitHub 上。之因此用它是由於它基於可靠的開源工具、自己也是開源的,同時它足夠簡單,簡單到我不須要它也能夠操做個人密碼。
也一直有想法爲它開發一個 UI, 因而今年九月我用 Electron 開發了一個名爲 Elecpass 的密碼管理器,在機制和數據格式上與 pass 徹底兼容。以前其實我並無用過 Electron, 但上手的體驗仍是至關不錯的,沒有遇到什麼問題。由於 Electron 自帶了 commonjs 的模塊加載系統,也再也不須要像前端開發那樣複雜的構建過程。
目前 Elecpass 一共發佈了兩個版本,雖然還很是簡陋並且有一些 Bug,但已經能夠知足基本需求了,我本身也一直在使用,明年我應該會爲它添加更多的功能。
今年年初騰訊發佈了微信小程序,我表明公司在「小小程序,大有做爲」的線下活動裏作了一個主題爲「在微信小程序中使用 LeanCloud」的分享,在準備期間我也瞭解了一下微信小程序。
能夠說微信小程序就是騰訊爲了在微信中構建一個封閉的「操做系統」的產物,但你們迫於微信自己的平臺能力,好比用戶信息、推送、支付,不得不使用它。做爲一個平臺,微信小程序綁定了一個數據綁定框架,也綁定了一套模板語言,同時和前端現有的工具鏈(編譯打包)的整合也很是差,很難利用現有的 JavaScript 生態。做爲結果,我相信微信小程序不會有什麼技術層面的社區和生態,只能做爲最末端的用戶界面。
年初由於發現我司的 服務狀態頁 年久失修,我決心重寫一個服務狀態頁,參考一下 GitHub 等網站。我但願它能同時展現三個節點的狀態、可以展現過去一天的歷史狀態、容許運維同事在服務狀態頁上快速地發佈通知。最後這個狀態頁也開源了出來,在 leancloud/leancloud-status。
爲了可以讓服務狀態頁自己老是保持可用,我設計了一個比較有趣的架構:後端(檢查器)分別運行在咱們三個節點的雲引擎上,交叉對全部節點進行檢查,將結果和展現歷史圖表所須要的數據寫入到 S3(或其餘對象存儲上);狀態頁面做爲靜態頁面託管在 CDN 上,從 S3 分別拉取三個節點的檢查結果和歷史圖表數據,對來自三個節點的數據進行彙總,決定顯示爲「正常」仍是「故障」。
這樣就保證了服務狀態頁自己的可用性和三個節點隔離,可用性僅依賴於 S3(理論上能夠同時寫入多個對象存儲做爲熱備),檢測程序又運行在咱們本身的雲引擎上(比單獨部署在一臺機器上更易於維護),架構又並不複雜。
爲了在前端合併三個節點的時序數據並繪製圖表,我實際上是費了很大的功夫的,但在實際部署的過程當中遇到了不少細節的問題,作了不少妥協。例如咱們的美國節點到國內的訪問一直不順暢等等,最後並無把我製做的歷史圖表展現出來。
以前幾個北京的同事寫了一個 聊天機器人 放在公司的 IM 上,天天看他們調戲機器人以爲挺幼稚的。但等我搬到北京以後也加入了他們的隊伍,我給機器人加了幾個有趣的功能,雖然實現上並不複雜,但你能夠經過聊天的方式把它展現給別人看,也可讓別人參與人來,仍是個很是有意思的事情。
首先我寫了一個 幫助你們決定晚上吃什麼 的功能,這一寫我就來了興趣,後來又寫了 確認你們是否都準備好吃晚飯了、幫助運維同事簡單地更新服務狀態頁,還 爲公司免費午飯的福利隨機人選。