Gin框架简洁版

shidawuhen · 2020-05-24 23:26:13 · 1884 次点击 · 预计阅读时间 4 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2020-05-24 23:26:13 的文章,其中的信息可能已经有所发展或是发生改变。

介绍

看完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

image-20200216115509091 image-20200216115509091

最后

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

qrcode_for_gh_7d704fb87cd1_258.jpg


有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1884 次点击  ∙  1 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传