笔记2 [实战项目] Go 语言笔记服务(上): 项目框架之Gin | 青训营笔记

258 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记。

在笔记服务(easy_note)中, api部分使用Gin框架处理收到的http请求. 然后我去网上查了一下, Gin框架是一种Go语言中常用的Web框架.为了更好地开发抖音项目,必须先学习一下Gin框架的原理和基本使用.

1. 什么是Gin框架

Gin 是一个用 Golang 语言编写的 Web 框架. 它具有类似 Martini 的 API,但性能比 Martini 快 40 倍.

2. Gin的特点

总接下来,Gin主要有以下特点:

  • 快速

    • 基于 Radix 树(一种更节省空间的 Trie 树结构)的路由,占用内存更少;

    • 没有反射;

    • 可预测的 API 性能。

  • 内置路由器

    • 开箱即用的路由器功能,不需要做任何配置即可使用。
  • 支持中间件

    • 比如日志,恢复等中间件;
  • 崩溃处理

    • Gin 可以捕获 HTTP 请求期间发生的panic并恢复它;
  • 路由分组

    • 支持通过路由群组来更好地组织路由,例如是否需要授权、设置 API 的版本等;

    • 这些群组可以无限制地嵌套而不会降低性能。

  • 中间件扩展

    • 可以方便的定义新的中间件;
  • JSON验证

    • 可以解析并验证 JSON 格式的请求数据(比如检查某个必须值是否存在).

3. Gin框架源码分析

默认的gin流程如下:

graph TD
gin.Default --> gin.New 
gin.New --> |使用中间件:Use| Logger
gin.New --> 路由注册 --> r.Run
gin.New --> |使用中间件:Use| Recovery

r.Run 默认的监听端口是8080.

3.1 gin.Default

func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

该函数创建默认的Engine实例, 然后引入Logger和Recovery中间件,这两个中间件的作用是:

  • Logger: 默认的日志处理, 会标准化和输出日志;

  • Recovery: 捕获异常并进行处理;

3.2 gin.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,
		RemoteIPHeaders:        []string{"X-Forwarded-For", "X-Real-IP"},
		TrustedPlatform:        defaultPlatform,
		UseRawPath:             false,
		RemoveExtraSlash:       false,
		UnescapePathValues:     true,
		MaxMultipartMemory:     defaultMultipartMemory,
		trees:                  make(methodTrees, 0, 9),
		delims:                 render.Delims{Left: "{{", Right: "}}"},
		secureJSONPrefix:       "while(1);",
		trustedProxies:         []string{"0.0.0.0/0", "::/0"},
		trustedCIDRs:           defaultTrustedCIDRs,
	}
	engine.RouterGroup.engine = engine
	engine.pool.New = func() any {
		return engine.allocateContext()
	}
	return engine
}

可以看到该函数是对Engine实例进行初始化设置.

这些设置就是我们经常会使用到的,比如:

  • 路由组;

  • 是否自动重定向;

  • 是否尝试修复当前请求路径;

  • 是否对路径值进行转义处理;

  • 等等.

3.3 路由注册

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,这样就构成了一个handler链;

  • 将当前我们注册的路由规则添加到相应的路由树中.其中路由规则包含http方法,路由路径和handers.

3.4 r.Run

func (engine *Engine) Run(addr ...string) (err error) {
   defer func() { debugPrintError(err) }()

   if engine.isUnsafeTrustedProxies() {
      debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
         "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
   }

   address := resolveAddress(addr)
   debugPrint("Listening and serving HTTP on %s\n", address)
   err = http.ListenAndServe(address, engine.Handler())
   return
}

启动gin的流程为: 解析地址,然后调用http.ListenAndServe,将解析得到的地址和engine实例的handler传进去.

到了这里, Gin就可以对外提供http服务了~

4. 笔记服务中Gin的使用

func main() {
	Init()
	r := gin.New()
	authMiddleware, _ := jwt.New(&jwt.GinJWTMiddleware{
		// 省略
	})

	v1 := r.Group("/v1")
	user1 := v1.Group("/user")
	user1.POST("/login", authMiddleware.LoginHandler)
	user1.POST("/register", handlers.Register)

	note1 := v1.Group("/note")
	note1.Use(authMiddleware.MiddlewareFunc())
	note1.GET("/query", handlers.QueryNote)
	note1.POST("", handlers.CreateNote)
	note1.PUT("/:note_id", handlers.UpdateNote)
	note1.DELETE("/:note_id", handlers.DeleteNote)

	if err := http.ListenAndServe(":8080", r); err != nil {
		klog.Fatal(err)
	}
}
  • 这里没有使用gin.Default(), 而是gin.New().这样的话就没有使用Logger和Recovery这两个中间件;

  • 这里"/v1"为主路由组,然后下面有"/user"和"/note"这两个子路由组;

  • 分别进行路由注册,根据路由转到相应的handler函数;

  • 开启http.ListenAndServe, 从而提供http服务.