go語言實戰嚮導
> 導語: 用 golang 實現後臺服務,你想要的都在這兒,有 webserver 最簡框架 (wsp),有互動式程式設計環境 (gop),有程式管理服務 (cmonitor),有 orm(orm),有本地 kv 快取 (lc),等等
使用 go 語言做後臺服務已經有 3 年了,通過專案去檢驗一個又一個的想法,然後不斷總結,優化,最終形成了自己的一整套體系,小到一個列印物件的方法,大到一個 web 後臺專案最佳實踐指導,這一點一滴都是在不斷的實踐中進化開來,以下內容是其中一小部分的彙報,各位看官如有興致,請前往 https://github.com/simplejia 關注最新的程式碼變更,希望和 golang 愛好者們共同探討,也感謝 xie 大提供這樣一個優秀的社群場所。
wsp (go http webserver)
實現初衷
- 簡單可依賴,充分利用 go 已有的東西,不另外增加複雜、難以理解的東西,這樣做的好處包括:更容易跟隨 go 的升級而升級,降低使用者學習成本
- yii 提供的 controller/action 的路由方式比較常用,在 wsp 裡實現一套
- java annotation 的功能挺方便,在 wsp 裡,通過註釋來實現過濾器方法的呼叫定義
- 不能因為 wsp 的引入而降低原生 go http webserver 的效能
使用場景
- 以 http webserver 方式對外提供服務
- 後臺介面服務
使用案例
- 大型網際網路社交業務
實現方式
- 路由自動生成,按要求(見 demo/controller/demo.go)提供 controller/action 的實現程式碼,wsp 執行後會分析專案程式碼,自動生成路由表並記錄在檔案 demo/WSP.go 裡,controller/action 定義程式碼必須符合函式定義:func(http.ResponseWriter, *http.Request),並且是帶 receiver 的 method demo_set.go
package controller
import (
"net/http"
"github.com/simplejia/wsp/demo/service"
)
// @prefilter("Login", {"Method":{"type":"get"}})
// @postfilter("Boss")
func (demo *Demo) Set(w http.ResponseWriter, r *http.Request) {
key := r.FormValue("key")
value := r.FormValue("value")
demoService := service.NewDemo()
demoService.Set(key, value)
json.NewEncoder(w).Encode(map[string]interface{}{
"code": 0,
})
}
// generated by wsp, DO NOT EDIT.
package main
import "net/http"
import "time"
import "github.com/simplejia/wsp/demo/controller"
import "github.com/simplejia/wsp/demo/filter"
func init() {
http.HandleFunc("/Demo/Get", func(w http.ResponseWriter, r *http.Request) {
t := time.Now()
_ = t
var e interface{}
c := new(controller.Demo)
defer func() {
e = recover()
if ok := filter.Boss(w, r, map[string]interface{}{"__T__": t, "__C__": c, "__E__": e}); !ok {
return
}
}()
c.Get(w, r)
})
http.HandleFunc("/Demo/Set", func(w http.ResponseWriter, r *http.Request) {
t := time.Now()
_ = t
var e interface{}
c := new(controller.Demo)
defer func() {
e = recover()
if ok := filter.Boss(w, r, map[string]interface{}{"__T__": t, "__C__": c, "__E__": e}); !ok {
return
}
}()
if ok := filter.Login(w, r, map[string]interface{}{"__T__": t, "__C__": c, "__E__": e}); !ok {
return
}
if ok := filter.Method(w, r, map[string]interface{}{"type": "get", "__T__": t, "__C__": c, "__E__": e}); !ok {
return
}
c.Set(w, r)
})
}
- wsp 分析專案程式碼,尋找符合要求的註釋(見 demo/controller/demo_set.go),自動生成過濾器呼叫程式碼在檔案 demo/WSP.go 裡,filter 註解分為前置過濾器 (prefilter)和後置過濾器(postfilter),格式如:@prefilter({json body}),{json body}代表傳入引數,符合 json array 定義格式(去掉前後的中括號),可以包含 string 值或者 object 值,filter 函式定義滿足:func (http.ResponseWriter, *http.Request, map[string] interface{}) bool,過濾器函式如下: method.go
package filter
import (
"net/http"
"strings"
)
func Method(w http.ResponseWriter, r *http.Request, p map[string]interface{}) bool {
method, ok := p["type"].(string)
if ok && strings.ToLower(r.Method) != strings.ToLower(method) {
http.Error(w, "405 Method Not Allowed", http.StatusMethodNotAllowed)
return false
}
return true
}
> filter 輸入引數 map[string] interface{},會自動設定"T",time.Time 型別,值為執行起始時間,可用於耗時統計,"C",{Controller}型別,值為{Controller}例項,可通過介面方式存取相關資料(這種方式存取資料較 context 方式更簡單實用),"E",值為 recover() 返回值,用於檢測錯誤並處理(後置過濾器必須 recover())
- 專案 main.go 程式碼示例 main.go
package main
import (
"log"
"github.com/simplejia/clog"
"github.com/simplejia/lc"
"net/http"
_ "github.com/simplejia/wsp/demo/clog"
_ "github.com/simplejia/wsp/demo/conf"
_ "github.com/simplejia/wsp/demo/mysql"
_ "github.com/simplejia/wsp/demo/redis"
)
func init() {
lc.Init(1e5)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
})
}
func main() {
clog.Info("main()")
log.Panic(http.ListenAndServe(":8080", nil))
}
miscellaneous
- 通過 wrk 壓測工具在同樣環境下(8 核,8g),wsp 空跑 qps:9 萬,beego1.7.1 空跑 qps:5.5 萬
- 更方便加入 middleware(func(http.Handler) http.Handler),其實更推薦通過定義過濾器的方式支援類似功能
- 更方便編寫如下的測試用例:
demo
- 提供一個簡單易擴充套件的專案 stub
實現初衷
- 簡單可依賴,充分利用 go 已有的東西,不另外增加複雜、難以理解的東西,這樣做的好處包括:更容易跟隨 go 的升級而升級,降低使用者學習成本
- 提供常用元件的簡單包裝,如下:
專案編寫指導意見
- 目錄結構:
├── WSP.go ├── clog │ └── clog.go ├── conf │ ├── conf.go │ └── conf.json ├── controller │ ├── base.go │ ├── demo.go │ ├── demo_get.go │ └── demo_set.go ├── demo ├── filter │ ├── boss.go │ ├── login.go │ └── method.go ├── main.go ├── model │ ├── demo.go │ ├── demo_get.go │ └── demo_set.go ├── mysql │ ├── demo_db.json │ └── mysql.go ├── redis │ ├── demo.json │ └── redis.go ├── service │ ├── demo.go │ ├── demo_get.go │ └── demo_set.go └── test ├── clog -> ../clog ├── conf -> ../conf ├── demo_get_test.go ├── demo_set_test.go ├── init_test.go ├── mysql -> ../mysql └── redis -> ../redis
- controller 目錄:負責 request 引數解析,service 呼叫
- service 目錄:負責邏輯處理,model 呼叫
- model 目錄:負責資料處理
- 介面實現上,建議一個介面對應一個檔案,如 controller/demo_get.go, service/demo_get.go, model/demo_get.go
更多原創文章乾貨分享,請關注公眾號
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- 【Go語言入門系列】(八)Go語言是不是面嚮物件語言?Go物件
- Go 語言實戰 GraphQLGo
- Go語言SQL操作實戰GoSQL
- 帶讀 |《Go in Action》(中文:Go語言實戰)(一)Go
- Go語言實戰(三)- 內建容器Go
- GO 語言 Web 開發實戰一GoWeb
- Go 語言實戰: 編寫可維護 Go 語言程式碼建議Go
- go語言實戰教程:Redis實戰專案應用GoRedis
- [譯] Go 語言實戰: 編寫可維護 Go 語言程式碼建議Go
- 有Go語言實戰培訓班嗎?go語言開發環境搭建Go開發環境
- 帶讀 |《Go in Action》(中文:Go語言實戰)語法和語言結構概覽 (二)Go
- 帶讀 |《Go in Action》(中文:Go語言實戰) 語法和語言結構概覽(三)Go
- 用 Go 語言實戰 Limit Concurrency 方法GoMIT
- 非常適合GO語言新手學習的《Go語言從入門到實戰——簡明高效的Go語言實戰指南》課程——推薦分享Go
- Go語言核心36講(Go語言實戰與應用七)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用八)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用二)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用四)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用五)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用十七)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用十八)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用九)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用十一)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用一)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用十九)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用二十)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用十二)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用十三)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用十四)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用十五)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用十六)--學習筆記Go筆記
- Go語言專案實戰:多人聊天室Go
- Go語言專案實戰:併發爬蟲Go爬蟲
- 帶讀 |《Go in Action》(中文:Go語言實戰)打包和工具鏈Go
- 帶讀 |《Go in Action》(中文:Go語言實戰) 深入瞭解切片Go
- Go語言核心36講(Go語言實戰與應用二十六)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用二十七)--學習筆記Go筆記
- Go語言核心36講(Go語言實戰與應用二十三)--學習筆記Go筆記