golangはゼロからミドルウェアを実現する
ミドルウェアの実装の背景
まず次のコードを見てください.
簡単なHTTPインタフェースサービスです
インタフェースにサービスの時間のかかる処理ログを追加する必要がある新しいニーズがあり、上記のプログラムを少量修正しました.
ビジネスが増加するにつれて、ルートは
この場合、各ルーティングにパラメータ情報ログを追加する必要がある場合、コードはよく記述されていますが、すべてのhandleにこのコードを変更する必要があり、後で追加されるhandleもこのコードを理解して追加する必要があります.コードのメンテナンスがますます面倒になります.したがって、ミドルウェアを使用して、ビジネスコードと非ビジネスコード(例えば、上記のインタフェースの時間消費統計、要求パラメータログなど)を分割する必要があります.
インプリメンテーションコード
ブラウザの入力:http://localhost:8080/path1
実行結果
結果を実行するスタックフロー
シーンの適用
参考資料
ginフレームワークソース中間価格実現
GO高度プログラミング
まず次のコードを見てください.
package main
func hello(wr http.ResponseWriter, r *http.Request) {
wr.Write([]byte("hello"))
}
func main() {
http.HandleFunc("/", hello)
err := http.ListenAndServe(":8080", nil)
...
}
簡単なHTTPインタフェースサービスです
インタフェースにサービスの時間のかかる処理ログを追加する必要がある新しいニーズがあり、上記のプログラムを少量修正しました.
func hello(wr http.ResponseWriter, r *http.Request) {
timeStart := time.Now()
wr.Write([]byte("hello"))
timeElapsed := time.Since(timeStart)
fmt.Println(timeElapsed)
}
ビジネスが増加するにつれて、ルートは
package main
func helloHandler(wr http.ResponseWriter, r *http.Request) {
// ...
}
func showInfoHandler(wr http.ResponseWriter, r *http.Request) {
// ...
}
func showEmailHandler(wr http.ResponseWriter, r *http.Request) {
// ...
}
func showFriendsHandler(wr http.ResponseWriter, r *http.Request) {
timeStart := time.Now()
wr.Write([]byte("your friends is tom and alex"))
timeElapsed := time.Since(timeStart)
logger.Println(timeElapsed)
}
func main() {
http.HandleFunc("/", helloHandler)
http.HandleFunc("/info/show", showInfoHandler)
http.HandleFunc("/email/show", showEmailHandler)
http.HandleFunc("/friends/show", showFriendsHandler)
// ...
}
この場合、各ルーティングにパラメータ情報ログを追加する必要がある場合、コードはよく記述されていますが、すべてのhandleにこのコードを変更する必要があり、後で追加されるhandleもこのコードを理解して追加する必要があります.コードのメンテナンスがますます面倒になります.したがって、ミドルウェアを使用して、ビジネスコードと非ビジネスコード(例えば、上記のインタフェースの時間消費統計、要求パラメータログなど)を分割する必要があります.
インプリメンテーションコード
package main
import(
"fmt"
"net/http"
"sync"
)
// HandlerFunc defines the handler used by middleware as return value.
type HandlerFunc func(*Context)
// HandlersChain defines a HandlerFunc array.
type HandlersChain []HandlerFunc
//
type Context struct {
Request *http.Request
Writer http.ResponseWriter
handlers HandlersChain
index int8
}
//
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
// HandlersChain
// c.Next()
// c.Next() c.Next() , c.Next()
c.handlers[c.index](c)
c.index++
}
}
func (c *Context) reset() {
c.handlers = nil
c.index = -1
}
//
type RouterGroup struct {
//
Handlers HandlersChain
engine *Engine
}
func (group *RouterGroup) Use(middleware ...HandlerFunc) {
group.Handlers = append(group.Handlers, middleware...)
}
func (group *RouterGroup) AddRoute(absolutePath string, handlers ...HandlerFunc) {
handlers = group.combineHandlers(handlers)
//
group.engine.addRoute(absolutePath, handlers)
}
//
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
finalSize := len(group.Handlers) + len(handlers)
mergedHandlers := make(HandlersChain, finalSize)
copy(mergedHandlers, group.Handlers)
copy(mergedHandlers[len(group.Handlers):], handlers)
return mergedHandlers
}
type Engine struct{
tree map[string]HandlersChain // tree map
RouterGroup
pool sync.Pool // ,
}
func NewEngine() *Engine {
engine := &Engine{
RouterGroup: RouterGroup{
Handlers: nil,
},
tree: make(map[string]HandlersChain),
}
engine.RouterGroup.engine = engine
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
return engine
}
func (engine *Engine) allocateContext() *Context {
return &Context{}
}
//url ,
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context)
c.Writer = w
c.Request = req
c.reset()
engine.handleHTTPRequest(c)
engine.pool.Put(c)
}
func (engine *Engine) handleHTTPRequest(c *Context) {
rPath := c.Request.URL.Path
handlers := engine.getValue(rPath)
if handlers != nil {
c.handlers = handlers
//
c.Next()
return
}
}
// HandlersChain
func (engine *Engine)getValue(path string)(handlers HandlersChain){
handlers,ok := engine.tree[path]
if !ok {
return nil
}
return
}
func (engine *Engine) addRoute(path string, handlers HandlersChain) {
engine.tree[path]=handlers
}
func (engine *Engine) Use(middleware ...HandlerFunc) {
engine.RouterGroup.Use(middleware...)
}
func main(){
engine := NewEngine()
engine.Use(func(c * Context){
fmt.Println("begin middle1")
fmt.Println("end middle1")
})
engine.Use(func(c * Context){
fmt.Println("begin middle2")
c.Next()
fmt.Println("end middle2")
})
engine.Use(func(c * Context){
fmt.Println("begin middle3")
c.Next()
fmt.Println("end middle3")
})
engine.AddRoute("/path1",func( c *Context){
fmt.Println("path1")
c.Writer.Write([]byte("path1"))
})
engine.AddRoute("/path2",func( c *Context){
fmt.Println("path2")
c.Writer.Write([]byte("path2"))
})
http.ListenAndServe(":8080", engine)
}
ブラウザの入力:http://localhost:8080/path1
実行結果
結果を実行するスタックフロー
シーンの適用
http
, /ping,/healthcheck,
, ,
X-Forwarded-For X-Real-IP, http.Request RemoteAddr RealIP
requestid, , ,
context.Timeout , http.Request
channel token, token
参考資料
ginフレームワークソース中間価格実現
GO高度プログラミング