Gin源码分析(一)官方demo分析
来源:互联网 发布:淘宝店铺信誉怎么算 编辑:程序博客网 时间:2024/06/06 01:43
目录:
- 目录
- 简介
- 官方demo
- 1 ginDefault
- 11 看到New函数
- 12 debugPrintWARNINGNew
- 13 新建engine
- 14 分配Context
- 2 engineUse
- 21 Logger
- 22 recovery
- 23 engineUse
- 3 ginGET
- 3 routerrun
简介
Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确。
最近一直在学习Go语言,迅速扫了一遍《Go语言圣经》,觉得只有理论没有实践可不行,因此,很喜欢简洁的gin,就先来看这个吧。
官方demo
首先看官方demo
package mainimport "github.com/gin-gonic/gin"func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and serve on 0.0.0.0:8080}
1.1 gin.Default
// Default returns an Engine instance with the Logger and Recovery middleware already attached.func Default() *Engine { engine := New() engine.Use(Logger(), Recovery()) return engine}
这段代码主要完成了以下内容:
- 新建一个engine
- 使用默认的log、recovery中间层
1.1.1 看到New()函数
func New() *Engine { debugPrintWARNINGNew() engine := &Engine{ RouterGroup: RouterGroup{ Handlers: nil, basePath: "/", root: true, }, FuncMap: template.FuncMap{}, RedirectTrailingSlash: true, RedirectFixedPath: false, HandleMethodNotAllowed: false, ForwardedByClientIP: true, AppEngine: defaultAppEngine, UseRawPath: false, UnescapePathValues: true, MaxMultipartMemory: defaultMultipartMemory, trees: make(methodTrees, 0, 9), delims: render.Delims{Left: "{{", Right: "}}"}, secureJsonPrefix: "while(1);", } engine.RouterGroup.engine = engine engine.pool.New = func() interface{} { return engine.allocateContext() } return engine}
New方法主要生成一个Engine的实例,并进行配置。
1.1.2 debugPrintWARNINGNew()
我们每次启动gin服务器,如果不加
gin.SetMode(gin.ReleaseMode)
这一段,就会输出一段提示
[WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode)
1.1.3 新建engine
type Engine struct { RouterGroup delims render.Delims secureJsonPrefix string HTMLRender render.HTMLRender FuncMap template.FuncMap allNoRoute HandlersChain allNoMethod HandlersChain noRoute HandlersChain noMethod HandlersChain pool sync.Pool trees methodTrees
engine是框架实例,主要包括:
- muxer 我也不知道是干嘛的,看面看到了再回头改吧
- 中间层 中间处理连接的过程,比如 logger、recovery
- 其他设置
1.1.4 分配Context
type Context struct { writermem responseWriter Request *http.Request Writer ResponseWriter Params Params handlers HandlersChain index int8 engine *Engine // Keys is a key/value pair exclusively for the context of each request. Keys map[string]interface{} // Errors is a list of errors attached to all the handlers/middlewares who used this context. Errors errorMsgs // Accepted defines a list of manually accepted formats for content negotiation. Accepted []string}
context是gin的一个重要组成部分。用来在中间层传递数据流。
1.2 engine.Use
engine.Use(Logger(), Recovery())
先看到Logger()
1.2.1 Logger()
这里定义了一个log中间层,在连接处理的过程中生成日志并输出
上函数:
func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc { isTerm := true if w, ok := out.(*os.File); !ok || (os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()))) || disableColor { isTerm = false } var skip map[string]struct{} if length := len(notlogged); length > 0 { skip = make(map[string]struct{}, length) for _, path := range notlogged { skip[path] = struct{}{} } } return func(c *Context) { // Start timer start := time.Now() path := c.Request.URL.Path raw := c.Request.URL.RawQuery // Process request c.Next() // Log only when path is not being skipped if _, ok := skip[path]; !ok { // Stop timer end := time.Now() latency := end.Sub(start) clientIP := c.ClientIP() method := c.Request.Method statusCode := c.Writer.Status() var statusColor, methodColor, resetColor string if isTerm { statusColor = colorForStatus(statusCode) methodColor = colorForMethod(method) resetColor = reset } comment := c.Errors.ByType(ErrorTypePrivate).String() if raw != "" { path = path + "?" + raw } fmt.Fprintf(out, "[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s", end.Format("2006/01/02 - 15:04:05"), statusColor, statusCode, resetColor, latency, clientIP, methodColor, method, resetColor, path, comment, ) } }}
这部分代码主要做了以下内容:
- 判断日志的输出,默认输出到 stdout
- 对收到的连接,输出日志,日志的内容包括:请求处理成功的时间戳、http返回状态码、耗时、请求IP、请求方法、请求路径、错误信息
1.2.2 recovery()
// RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.func RecoveryWithWriter(out io.Writer) HandlerFunc { var logger *log.Logger if out != nil { logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags) } return func(c *Context) { defer func() { if err := recover(); err != nil { if logger != nil { stack := stack(3) httprequest, _ := httputil.DumpRequest(c.Request, false) logger.Printf("[Recovery] panic recovered:\n%s\n%s\n%s%s", string(httprequest), err, stack, reset) } c.AbortWithStatus(500) } }() c.Next() }}
这一块主要利用了 Go语言的recovery,实现从错误中回复。
当请求出现错误时,造成程序报错,将在此恢复,输出日志并返回 状态码500
1.2.3 engine.Use()
这里主要应用中间层
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { engine.RouterGroup.Use(middleware...) engine.rebuild404Handlers() engine.rebuild405Handlers() return engine}
这里在 engine的 RouterGroup中添加了中间层。
404he 405 使用 noRoute 和 noMethod
1.3 gin.GET()
// GET is a shortcut for router.Handle("GET", path, handle).func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes { return group.handle("GET", relativePath, handlers)}
这里仔细看,这里的 方法 接受者 是 RouterGroup 并不是 Default 返回的 Engine。
再去看 Engine的定义
type Engine struct { RouterGroup delims render.Delims secureJsonPrefix string HTMLRender render.HTMLRender FuncMap template.FuncMap allNoRoute HandlersChain allNoMethod HandlersChain noRoute HandlersChain noMethod HandlersChain pool sync.Pool trees methodTrees RedirectTrailingSlash bool RedirectFixedPath bool HandleMethodNotAllowed bool ForwardedByClientIP bool AppEngine bool UseRawPath bool UnescapePathValues bool MaxMultipartMemory int64}
这里使用到了 struct 匿名成员的特性。RouterGroup定义为匿名成员,也就是Engine继承了RouterGroup。
因此Engine可以调用RouterGroup的方法。
再看到group.handle 方法,
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { absolutePath := group.calculateAbsolutePath(relativePath) handlers = group.combineHandlers(handlers) group.engine.addRoute(httpMethod, absolutePath, handlers) return group.returnObj()}
主要完成了以下内容:
- 计算出绝对路径
- 将handlers加入HandlersChain
- 添加路由,这里有一颗方法树,在方法树中添加 该路由
1.3 router.run
func (engine *Engine) Run(addr ...string) (err error) { defer func() { debugPrintError(err) }() address := resolveAddress(addr) debugPrint("Listening and serving HTTP on %s\n", address) err = http.ListenAndServe(address, engine) return}
这里首先判断端口,默认为8080端口,
这里调用了 go语言的 net包中的http的 ListenAndServe
至此,官方的 启动demo分析完成。
这只是我对于gin的源码分析的第一个版本,还有很多不足,后续会在此基础上进行完善,如有错误欢迎指出。
- Gin源码分析(一)官方demo分析
- vitamio官方demo源码分析(1)——MediaPlayerDemo_Video.java分析
- vitamio官方demo源码分析(1)——MediaPlayerDemo_Video.java分析
- vitamio官方demo源码分析(1)——MediaPlayerDemo_Video.java分析
- Google官方Demo NavigationDrawer 侧边导航源码分析
- jsPlumb Demo源码分析
- demo\netease\源码分析
- 源码分析(一)
- vitamio官方demo源码分析1——MediaPlayerDemo_Video.java分析
- MVP google官方demo比较分析
- Android-Architecture-Componets官方DEMO分析
- amq 源码分析之demo分析1
- JUnit源码分析(一)
- osworkflow源码分析(一)
- Log4net源码分析(一)
- Mangos源码分析(一)
- Notepad++源码分析(一)
- Log4net源码分析(一)
- c语言枚举类型
- Java运行时遇到的一些错误
- Excel导入导出工具类
- 将redis制作成systemctl服务
- JSP指令
- Gin源码分析(一)官方demo分析
- Numpy np.dot() vs np.multiply() vs *
- C#梳理【数组Array】
- Qt文件操作问题
- 求高精度幂
- numpy.random
- 高性能服务器程序框架
- 结构体
- 根据关键字搜索某文件夹下的文件