咱們是「十二贊」,一個致力於幫助電商賣家上馬和運營小程序的小團隊,咱們的主頁是www.12zan.cn/。在實際運行中,咱們使用了大量由golang寫就的小工具,幾乎每個工具代碼量都超短,通常在200行左右就完成了一個獨立的功能,同時擔當了至關重要的角色;像代理服務器,代碼量一共500行多一點點,倒是咱們的核心支柱,壓測時QPS也直追nginx,表現優異。php
作爲基礎架構,我介紹一下咱們的機器架構。
咱們的整個業務構建於阿里雲之上,有5臺server,每一對都有獨立的外網IP,同時也在同一個內網之中。在每一臺機器上都跑了一個咱們本身用golang寫的守護進程,這個進程負責監聽一些業務重啓、新增域名等相似的指令並執行(這些指令最後都傳遞給了docker)。同時,每臺機器上都有一個consul進程,這些consul都join到了一塊兒。另外,咱們每一臺機器上,都用docker跑了一個nginx來作80端口的服務,同時跑了一個用golang本身寫的HTTP代理。nginx作作日誌啊基礎的功能以後就把請求丟給這個http代理 ,HTTP代理會到consul裏去查應該將請求轉發到哪一個IP的哪一個端口上。java
在架構選型的第一天,咱們就決定,咱們會服務化,會大量使用http 接口來提供服務,並使用本身的http proxy來分發請求、添加自有的一些業務邏輯好比API的權限驗證等邏輯。咱們限定,全部業務,域名都是*.app.12zan.net,好比咱們要上一個聊天服務,請求的接口就會是chat.app.12zan.net,哪天再上個評論服務,請求的接口就是comment.app.12zan.net。
肯定域名後我第一件事情,就是拿golang本身寫了一個很是簡單的基於consul的http proxy server;感謝Golang這完善的HTTP庫,咱們只用了幾百行代碼就完成了全部功能。每當有http請求過來時,這個proxy server就會根據HTTP請求中HTTP_HOST 字段去consul去查,有哪些後端是用這個域名名稱來註冊服務的,並根據指定的算法,取出一臺後端來,把這個HTTP請求Proxy過去。每一個具體的業務,可能運行在咱們5臺機器中的任何一臺之中的docker上,也多是多個docker實例上。因此這裏有一個機制,選擇哪一個實際的docker實例來服務這個請求的問題。咱們如今支持隨機選取、按客戶端IP地址作hash以後選取、按URL作Hash選取、按負載選取幾種方式。node
基於php+laralel和nodejs+koajs兩種場景,咱們製做了本身的docker鏡像。這個鏡像除開能夠將php+nginx和nodejs構建的web服務運行起來以外,還包含一個golang寫的consul客戶端。在docker容器裏,這個客戶端隨着php+nginx或是nodejs的web服務一塊兒啓動,啓動以後會向宿主機的consul 進程註冊本身這個服務,註冊的時候會通知說,某某應用,在某某IP某某端口提供服務啦,若是前面有到**.app.12zan.net的請求你能夠轉發給我;同時會每隔一秒上報本身的進程數、當前機器CPU佔用、內存佔用狀況。也是同樣的簡單,幾百行golang代碼,就鼓搗出了這個consul客戶端。爲何使用golang呢?第一個緣由固然是由於consul天生是golang陣營,第二個,是由於咱們的docker容器種類較多,因此這個客戶端直接就是在Mac上跨平臺編譯出來的在linux64平臺上運行的,無論docker容器是python爲基準的仍是ruby爲基準的,仍是nodejs的,只要把這個二進制文件拷貝進去就能正確運行,不像別的語言須要解決依賴問題。python
咱們還開發了一個web console界面,在這裏,咱們能夠註冊app,也能夠爲app新增實例。註冊app時,咱們要指定代碼倉庫的地址(對了,咱們的代碼管理是用的golang寫的gogs),指定對外服務的域名,指定是nodejs應用仍是php+laravel應用。添加應用以後,能夠在這個應用下新建實例,讓系統在指定的IP上去跑這個實例。實例運行的過程實際就是下發一個通知到某個機器上,去執行一個docker實例啓動的過程。docker啓動的時候帶了一些環境變量,好比當前內網IP、docker監聽的端口、對外提供服務時是用何域名提供服務。mysql
前面這種架構有一個問題,就是後端多是在任何一臺機器上運行的,今天多是A,明天多是B,那我要是把文件存在A上了是否是讓B來提供服務的時候就掛掉了?因此咱們想了這麼一個辦法(也是由於窮。。。。),咱們把全部的文件都挪到阿里雲的OSS服務上。同時爲了避免管是Nodejs應用仍是php應用 仍是python寫的應用都能作到把用戶上傳的文件或是系統生成的文件存到oss上面,咱們很省事地寫了一個ossUploader,編譯好的可執行文件發佈,只須要執行它,傳進來本地路徑和oss上的目標路徑,就保證給你上傳到oss上去就完整,不須要再在nodejs、php、python、ruby、java各類平臺下都琢磨一遍oss的SDK。linux
對日誌的處理是同樣的, 全部的日誌文件的內容,都會被一個golang寫的工具gtail監聽着(就像linux 的tail -f命令同樣),全部新產生的內容都會被gtail挪到oss上去存儲。固然,也是可執行文件發佈的。nginx
這個實現以後, 咱們的實際業務就真正能夠在5臺機器上之間任意騰挪了。laravel
得益於golang的一些開源倉庫,咱們還作了一些好玩的東西。
好比,看到github.com/gorilla/web…這個東東,咱們忍不住擼了一個websocket server,或是說叫羣聊服務器更好一點。
接下來,咱們看到有一個golang的庫叫go-mysql-elasticsearch,假裝了一個mysql的slave,去MySQL的master機器上去讀binlog,讀到binlog之後就將MySQL裏的數據發送給ElasticSearch去索引數據。
咱們就結合了一個,把這兩個結合起來,修改了一下go-mysql-elastichsearch,讓它監聽到MySQL的數據變動以後,在WebSocket server的某個羣聊裏推送出來,造成一個數據變動的廣播。
再接下來咱們就能夠用nodejs寫一個應用,連上這個websocket server,加入特定的某個羣聊,就源源不斷地收聽到數據變動的消息。這個nodejs端的代碼就很是簡潔了,只須要不到100行代碼能夠作各類好玩的事情,好比監聽到用戶留言表有新增,能夠發郵件讓運營立刻去審覈。還有好比說,每當訂單表有成交的時候,咱們某個小小的nodejs應用由於監聽了數據庫消息,第一時間就知道了,立刻就去追溯用戶來源,來計算返利;同時這個nodejs的代碼更新是和訂單主邏輯徹底不相關的,寫這個業務的開發人員只須要知道訂單表的結構,不須要了解訂單應用後臺代碼。git