Gin Web 框架使用和流程分析

Gin Web 框架的使用和流程分析

Demo

package main

import (
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
)

func HelloWorld(c *gin.Context) {
	c.String(http.StatusOK, "pong")
}

func main() {
    
	r := gin.Default()  // 获取一个Gin的实例:Engine,gin的核心数据结构	
    r.Use(gin.Logger(), gin.Recovery())  // 使用中间键
    r.GET("/ping", HelloWorld) // 注册路由
	port := "6969"
	log.Printf("Listening on port %s", port)
	r.Run(":" + port)  // 开始监听端口,提供服务
}

Gin 请求生命周期

Gin的数据结构

在原生的http 服务中,我们知道,只要实现了 Handler 接口就可以提供HTTP 服务,在gin中,他也实现了 Handler 接口 ,其实,就是因为gin 的 Engine 实现了 Handler 的接口,他就和 原生的 http 接口相关联了起来

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	c := engine.pool.Get().(*Context)
	c.writermem.reset(w)
	c.Request = req
	c.reset()

	engine.handleHTTPRequest(c)

	engine.pool.Put(c)
}

type Engine struct {
	RouterGroup // 路由组,注册的路径和业务处理函数就在这里
 
    // ... 省略代码
	pool             sync.Pool // context Pool
	trees            methodTrees // 按照请求的HTTP method进行了分组
}

简单了解下 Gin 服务执行流程

Gin 生命周期
创建 Gin 实例
r := gin.Default()  // 获取一个Gin的实例:Engine,gin的核心数据结构

这里获取了一个默认的 gin的实例

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New() // 默认实例
	engine.Use(Logger(), Recovery()) // 注册中间建,中间件的是一个函数,最终只要返回一个 type HandlerFunc func(*Context) 就可以
	return engine
}
```go

 #### 路由注册
 ```go
r.GET("/", func(c *gin.Context) { // 注册路由, 实际上是调用了 RouterGroup 的Get方法, RouterGroup 实现了 IRoutes interface,RouterGroup 主要提供路有注册功能
		c.String(http.StatusOK, "Hello World!")
	})	

// GET is a shortcut for router.Handle("GET", path, handle).
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
	return group.handle(http.MethodGet, relativePath, handlers)
}

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
	absolutePath := group.calculateAbsolutePath(relativePath)
	handlers = group.combineHandlers(handlers) // 注意这个 combineHandlers,这里会把 r.Use(gin.Logger(), gin.Recovery()) 注册的中间键跟业务的处理函数组合在一起
	group.engine.addRoute(httpMethod, absolutePath, handlers)
	return group.returnObj()
}

func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
	finalSize := len(group.Handlers) + len(handlers)
	if finalSize >= int(abortIndex) {
		panic("too many handlers")
	}
	mergedHandlers := make(HandlersChain, finalSize)
	copy(mergedHandlers, group.Handlers) // 中间键
	copy(mergedHandlers[len(group.Handlers):], handlers) // 业务处理
	return mergedHandlers
}

// IRoutes defines all router handle interface.
type IRoutes interface {
	Use(...HandlerFunc) IRoutes

	Handle(string, string, ...HandlerFunc) IRoutes
	Any(string, ...HandlerFunc) IRoutes
	GET(string, ...HandlerFunc) IRoutes
	POST(string, ...HandlerFunc) IRoutes
	DELETE(string, ...HandlerFunc) IRoutes
	PATCH(string, ...HandlerFunc) IRoutes
	PUT(string, ...HandlerFunc) IRoutes
	OPTIONS(string, ...HandlerFunc) IRoutes
	HEAD(string, ...HandlerFunc) IRoutes

   // ... 省略代码

}
接收请求并响应
// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	c := engine.pool.Get().(*Context)
	c.writermem.reset(w)
	c.Request = req
	c.reset()

	engine.handleHTTPRequest(c)

	engine.pool.Put(c)
}

func (engine *Engine) handleHTTPRequest(c *Context) {

     // ... 省略代码

	// Find root of the tree for the given HTTP method
    // x
	t := engine.trees
	for i, tl := 0, len(t); i < tl; i++ {
		if t[i].method != httpMethod {
			continue
		}
		root := t[i].root
		// Find route in tree
		value := root.getValue(rPath, c.params, unescape)
		if value.params != nil {
			c.Params = *value.params
		}
        // 找到处理的业务处理函数
		if value.handlers != nil {
			c.handlers = value.handlers
			c.fullPath = value.fullPath
			c.Next()
			c.writermem.WriteHeaderNow()
			return
		}
        // ... 省略代码
}

func (c *Context) Next() {
	c.index++
	for c.index < int8(len(c.handlers)) {
		c.handlers[c.index](c) // 业务处理,也就是这里的 func HelloWorld(c *gin.Context), 也会跑中间键
		c.index++
	}
}

func HelloWorld(c *gin.Context) {
	c.String(http.StatusOK, "pong")
}
如何写自己的中间键

可以参考 gin. Logger, 只要你的中间键,最后的返回结果是一个函数,函数类型为:HandlerFunc 就可以

func Logger() HandlerFunc {
	return LoggerWithConfig(LoggerConfig{})
}

type HandlerFunc func(*Context)

Gin 整个流程如下图

在这里插入图片描述

如何和原生的 http 包结合起来

前面我们提到只要一个结构体只要实现了 Handler 接口就可以提供HTTP 服务,gin实现了 Handler 接口 。所以它可以和原生的 http 包结合起来。

整个流程就会变成如下图:
在这里插入图片描述

最底层的 net.Listener 在监听 TCP 段口,net库将接收到的请求给 http.Server , http.Server0最终会调用服务器实现的 ServerHTTP 函数,因为 gin 实现了 ServerHTTP 函数,所以最终处理请求会交给 gin 来处理

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CoLiuRs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值