Gin框架简洁版

介绍

看完Gin源码后,我在想是否可以尝试自己写一个简单的web-go框架,既能帮助自己更好的理解Gin,又能当做一个测验,检测自己是否真的学会了一些东西。

所以自己做了一个简单版的框架,这个框架包含了一些Gin框架的核心内容,感兴趣的朋友可以先看这个简单版的框架,然后再看我写的gin源码剖析,可能会更加容易理解。

写这个小框架的时候,突然想起了以前看侯捷的深入浅出MFC,有一章的标题叫-MFC六大关键技术之仿真。这个简洁版也算是gin框架的一个仿真吧。

框架写的很简单,主要是为了学习目的,可能有很多的漏洞,如果大家发现的话,可以告诉我进行更改。

代码路径

https://github.com/shidawuhen/asap/tree/feature_pzq_simpleframe

源码

main.go

package main

import (
   _ "asap/docs"
   "fmt"

   f "asap/framework"
)

func ping(c *f.Context) {
   fmt.Println(1)
   c.String("%s", "ping")
}

func main() {
   e := f.New()
   e.AddRoute("GET", "/ping", ping)
   e.Run("127.0.0.1:9090")
}

framework.go

package framework

import (
   "net/http"
   "sync"

   "fmt"
)

type HandlerFunc func(*Context)
type HandlersChain []HandlerFunc

//Context,用于处理请求的输入和输出数据
type Context struct {
   Request *http.Request
   Writer  http.ResponseWriter
}

//将请求返回值写入http.Responsewriter中
func (c *Context) String(format string, data ...interface{}) {
   fmt.Fprintf(c.Writer, format, data...)
   return
}

//核心结构,存放路由规则和使用pool获取与释放Context,减少GC
type Engine struct {
   pool   sync.Pool
   router map[string]map[string]HandlersChain
}

//Engine初始化
func New() *Engine {
   fmt.Println("start")
   engine := &Engine{}
   engine.pool.New = func() interface{} {
      return engine.allocateContext()
   }
   engine.router = make(map[string]map[string]HandlersChain, 0)
   return engine
}

func (engine *Engine) allocateContext() *Context {
   return &Context{}
}

//请求过来时http包会调用该函数
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   c := engine.pool.Get().(*Context)
   //1.context初始化
   c.Writer = w
   c.Request = req
   //2.真正的处理请求逻辑
   engine.handleHTTPRequest(c)

   engine.pool.Put(c)
}

func (engine *Engine) handleHTTPRequest(c *Context) {
   httpMethod := c.Request.Method
   rPath := c.Request.URL.Path
   //从router中找到对应的方法并执行,如果不存在,则直接返回
   routers, ok := engine.router[httpMethod]
   if ok {
      handles, ok := routers[rPath]
      if ok {
         for _, handle := range handles {
            handle(c)
         }
         return
      }
   }
   c.String("%s", httpMethod+" "+rPath+" doesnt exists")
   return
}

//将路由添加到router中,没有并发操作,所以不加锁
func (engine *Engine) AddRoute(method, path string, handlers ...HandlerFunc) {
   //1.判断该http方法是否存在
   _, ok := engine.router[method]
   if !ok {
      engine.router[method] = make(map[string]HandlersChain, 0)
   }
   //2.判断该路径是否存在,如果不存在则插入,如果存在,则不处理
   _, ok = engine.router[method][path]
   if !ok {
      engine.router[method][path] = handlers
   }
}

//运行服务,监听请求
func (engine *Engine) Run(address string) (err error) {
   err = http.ListenAndServe(address, engine)
   return
}

调用关系

这次修改,使得包之间的调用关系很清晰

执行:go-callvis -group pkg,type -ignore fmt,net/http,sync myproject/src

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章