go server框架學習之路 – 寫一個自己的go框架
go server框架學習之路 – 寫一個自己的go框架
1 創建一個簡單的框架
程式碼
package https import "net/http" // 創建自己的引擎 type Engine struct { } // 實現engine的ServeHTTP 有了這個方法 engine就屬於一個http的handle了 func (e *Engine)ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write([]byte("HTTP/1.0 200 OKrn" + "rn" + "你好中國")) } func Run() { http.ListenAndServe(":8080",&Engine{}) }
測試程式碼和執行結果:
package main import "gcw/pkg/https" func main() { https.Run() } **************************************** HTTP/1.0 200 OK 你好中國
2 封裝請求和響應
程式碼
package https import "net/http" // 統一上下文 以後寫自己的 視圖函數就不用寫一長串參數了 type Context struct { Request *http.Request Writer http.ResponseWriter } // 定義自己的HandlerFunc type HandlerFunc func(*Context) // 實現自己的HandlerFunc的ServeHTTP方法 func (f HandlerFunc) ServeHTTP(c *Context) { f(c) } // 創建自己的引擎 type Engine struct {} // 實現engine的ServeHTTP 有了這個方法 engine就屬於一個http的handle了 func (e *Engine)ServeHTTP(w http.ResponseWriter, r *http.Request) { c := &Context{ Request: r, Writer: w, } testHandlerFunc(c) } func testHandlerFunc(c *Context) { c.Writer.Write([]byte("測試2")) } func Run() { //http.Handle("/", &Engine{}) http.ListenAndServe(":8080",&Engine{}) }
測試程式碼和執行結果:
測試程式碼同1 ************************ 測試2
3 實現路由功能
程式碼
package https import "net/http" // 統一上下文 以後寫自己的 視圖函數就不用寫一長串參數了 type Context struct { Request *http.Request Writer http.ResponseWriter index int8 // 配合next 執行handlers handlers []HandlerFunc // } // 執行下一個handle func (c *Context) Next() { c.index++ for c.index < int8(len(c.handlers)) { c.handlers[c.index](c) c.index++ } } // 定義自己的HandlerFunc type HandlerFunc func(*Context) // 實現自己的HandlerFunc的ServeHTTP方法 func (f HandlerFunc) ServeHTTP(c *Context) { f(c) } // 路由 type Router struct { method string root string handles []HandlerFunc } // 往engine 的 routers 中添加路由 func (e *Engine)AddRouter(method string, path string , h []HandlerFunc) { e.routers[method+"_"+path] = &Router{ method: method, root: path, handles: h, } } func (e *Engine)Get(path string, h ...HandlerFunc) { e.AddRouter("GET", path, h) } func (e *Engine)POST(path string, h ...HandlerFunc) { e.AddRouter("POST", path, h) } // 創建自己的引擎 type Engine struct { routers map[string]*Router } // 通過new 創建engine 初始化參數 func New() *Engine{ return &Engine{routers:make(map[string]*Router)} } // 實現engine的ServeHTTP 有了這個方法 engine就屬於一個http的handle了 func (e *Engine)ServeHTTP(w http.ResponseWriter, r *http.Request) { method := r.Method path := r.RequestURI //remoteAddr := r.RemoteAddr router := e.routers[method+"_"+path] c := &Context{ Request: r, Writer: w, index: -1, handlers: router.handles, } c.Next() } func (e *Engine)Run() { //http.Handle("/", &Engine{}) http.ListenAndServe(":8080",e) }
測試程式碼和執行結果:
package main import "gcw/pkg/https" func main() { app := https.New() app.Get("/hello", testHandlerFunc) app.Run() } func testHandlerFunc(c *https.Context) { c.Writer.Write([]byte("測試3")) }
4 完善路由404
程式碼
// 實現engine的ServeHTTP 有了這個方法 engine就屬於一個http的handle了 func (e *Engine)ServeHTTP(w http.ResponseWriter, r *http.Request) { method := r.Method path := r.RequestURI //remoteAddr := r.RemoteAddr router := e.routers[method+"_"+path] if router == nil { w.Write([]byte("404 你訪問的頁面不存在")) return } c := &Context{ Request: r, Writer: w, index: -1, handlers: router.handles, } c.Next() }
測試程式碼和執行結果:
404 你訪問的頁面不存在
5 日誌列印功能
新建logger.go
package https import ( "fmt" "path" "runtime" "time" ) type loggerMessage struct { Millisecond int64 `json:"timestamp"` MillisecondFormat string `json:"time_format"` LevelString string `json:"level_string"` Body string `json:"body"` Position string `json:"position"` } type Logger struct { } func (logger *Logger) Writer(level string, msg string){ funcName := "null" pc, file, line, ok := runtime.Caller(2) if !ok { file = "null" line = 0 } else { funcName = runtime.FuncForPC(pc).Name() } _, filename := path.Split(file) loggerMsg := &loggerMessage{ Millisecond: time.Now().UnixNano() / 1e6, MillisecondFormat: time.Now().Format("2006-01-02 15:04:05.999"), LevelString: level, Body: msg, Position: fmt.Sprintf("%s %d %s", filename, line, funcName), } logger.OutPut(loggerMsg) } func (logger *Logger)OutPut(msg *loggerMessage) { fmt.Println(msg) } func (logger *Logger)Info(msg string) { logger.Writer("info", msg) }
engine 和 new中添加初始化log
func New() *Engine{ return &Engine{ routers:make(map[string]*Router), log:Logger{}, } } type Engine struct { routers map[string]*Router log Logger }
使用 及效果
tts = time.Now().UnixNano() - tts e.log.Info(fmt.Sprintf("%s %s %s %3.6fs",remoteAddr, method, path, float64(float64(tts)/1e9))) func (e *Engine)Run() { //http.Handle("/", &Engine{}) e.log.Info("伺服器啟動: 127.0.0.1:8080") http.ListenAndServe(":8080",e) } *************************************** &{1586071294752 2020-04-05 15:21:34.752 info 伺服器啟動: 127.0.0.1:8080 server.go 79 gcw/pkg/https.(*Engine).Run} &{1586071298492 2020-04-05 15:21:38.492 info 127.0.0.1:54446 GET /hello 0.000000s server.go 66 gcw/pkg/https.(*Engine).ServeHTTP}