## 介绍
看完Gin源码后,我在想是否可以尝试自己写一个简单的web-go框架,既能帮助自己更好的理解Gin,又能当做一个测验,检测自己是否真的学会了一些东西。
所以自己做了一个简单版的框架,这个框架包含了一些Gin框架的核心内容,感兴趣的朋友可以先看这个简单版的框架,然后再看我写的[gin源码剖析](https://studygolang.com/articles/28836),可能会更加容易理解。
写这个小框架的时候,突然想起了以前看侯捷的深入浅出MFC,有一章的标题叫-MFC六大关键技术之仿真。这个简洁版也算是gin框架的一个仿真吧。
框架写的很简单,主要是为了学习目的,可能有很多的漏洞,如果大家发现的话,可以告诉我进行更改。
## 代码路径
https://github.com/shidawuhen/asap/tree/feature_pzq_simpleframe
## 源码
main.go
```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
```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
<img src="https://static.studygolang.com/200524/6c12d0257c4b4f2858546f1ff3cb79b0.png" alt="image-20200216115509091" style="zoom:40%;" />
<img src="https://static.studygolang.com/200524/d78b63dff8549c1b9a3b40da03448053.png" alt="image-20200216115509091" style="zoom:40%;" />
## 最后
大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
![qrcode_for_gh_7d704fb87cd1_258.jpg](https://static.studygolang.com/200524/3f0fb1db65ef47c459b6fc1fea280d4a.jpg)
有疑问加站长微信联系(非本文作者))