GoWeb开发套装、第三方开源库和Go module原理| 青训营笔记

105 阅读6分钟

二、GoWeb开发套装、第三方开源库和Go module原理

1.简单介绍一下开发一个web项目需要处理的事件:路由匹配、参数获取、ORM持久化操作、日志,或更高级的redis、Elasticsearch和rpc远程调用

2.若是Golang原生web框架,需要做一个处理函数和监听端口函数,如下

package main

import (
   "fmt"
   "go-web-tutorial/handler"
   "log"
   "net/http"
)

func main() {
   fmt.Println("Starting the server ...")
   helloHandler := handler.NewHelloHandler()
   http.Handle("/hello", helloHandler)
   // 创建服务器,ListenAndServe若服务器宕机,会返回异常
   log.Fatal(http.ListenAndServe("localhost:8080", nil))
}

package handler

import (
	"bytes"
	"fmt"
	"net/http"
)

type HelloHandler struct {
	m map[string]string
}

func NewHelloHandler() *HelloHandler {
	return &HelloHandler{m: make(map[string]string)}
}

// 回声服务器,返回接受的body,
// 实现Handler接口
// 读取请求体内容存储在bytes.Buffer中,打印方法、请求的URI和请求体内容到标准输出
// 将读取到的请求体内容作为响应返回客户端,调用writer.Write方法将
//内容写出http.ResponseWriter对象完成HTTP响应
func (h HelloHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	b := request.Body
	buf := bytes.Buffer{}
	buf.ReadFrom(b)
	b.Close()
	s := buf.String()
	fmt.Printf("get request: \nMethod:%s\n%s\n%s\n", request.Method, request.RequestURI, s)
	writer.Write(buf.Bytes())
}

问题:

  1. 路由需要拦截下才知道请求方法

  2. 手动处理io

  3. 读取io的body手动映射到实例

  4. 连接参数需要手动获取

  5. 路由分组繁琐

  6. 返回值需要手动处理为[]byte类型

  7. 改进理由系统-Gin框架路由实际采用httprouter框架github.com/julienschmi…

    • httprouter支持配置CORS,可以利用Go函数闭包(匿名函数),将原有逻辑封装一层,达到预处理的效果
    • PS:CORS(跨源资源共享)是一种浏览器机制,用于在跨域请求中控制对资源的访问。跨域请求指的是,在浏览器中,从一个源(域、协议或端口)发起的请求,请求目标位于另一个源通过在服务器端设置CORS头信息,可以告知浏览器是否允许跨域请求的访问。HTTPRouter是一个支持配置CORS的Go语言路由器库,可以通过设置CORS头信息来处理CORS预检请求。

4.引出Gin框架,用Go编写的HTTP web 框架

1.引入

go get -u [github.com/gin-gonic/gin](<http://github.com/gin-gonic/gin>)

2.创建路由引擎

engine := gin.New()
或
engine := gin.Default()
//1、gin.New()路由引擎是一个空的引擎实例,没有任何默认的中间件或者配置
//适合场景:需要自定义配置,完全控制路由引擎
//2、gin.Default()带有默认配置和中间件的引擎实例,预先配置了日志记录、恢复和错误处理等。
//默认开启了Logger和Recovery中间件

PS:

  1. Gin中间件是什么-处理HTTP请求和响应的功能模块,可以在请求到达处理程序之前或响应返回给客户端之前对请求和响应进行一些额外的处理。

  2. 中间件函数在 Gin 中被定义为 func(c *gin.Context) 类型的函数。它接收一个 *gin.Context 对象作为参数,可以读取请求信息、设置响应信息,甚至中断请求或提前返回响应。

  3. 常见用途:认证和授权、日志记录、错误处理、请求处理前的预处理、响应处理后的后处理

  4. 两个默认的middleware: Logger、Recovery。

    1. Logger Middleware可以用于ELK收集metrics,例如,如果有ELK系统处理日志,则可以将Logger中间件中的Metrics以特定格式存放至特定Metrics日志文件中,后期给ELK分析。
    2. Gin 支持自动从Panic中恢复的功能,这是因为在Recovery Middleware中对Panic做了recovery()动作。

⚠️:Gin仅能帮你Recover handler中的错误,不能recover Gorouting中的Panic→ 任何gorouting中发生了panic,都会panic整个程序。每个gorouting需要自己处理panic

  1. Gin Router分组配置

    1. 父子Group共用engine
    2. 子获取父Handler当前Handler ,这个Handler其实就是调用链信息,这个特性使得我们可以在合适的组大小插入中间件

    ⚠️:创建子Group时,会调用拷贝当前路径,若在子Group创建后,父Group又添加了中间件进去,那么就没办法同步了!

    1. 子Group计算自己绝对路径

⚠️:所有的路由都是挂在gin.Engine下的,只有使用RouterGroup.GET,POST等方法才能注册路由,这也是所有Group都会保存engine的原因

  1. 路由及记录方法

    1. gin调用路由时,会取出gin里面的调用链,然后遍历执行

    2. 路由也是一个HanderFunc,但

      1. 注册路由通过GET,POST等方法
      2. 路由注册会计算Hander的绝对地址
      3. 路由注册会取出Group中的调用链,而不是塞进去
      4. 路由会有一系列操作记录在内部的树型结构上
    3. Gin路由是一棵树,根结点是以Http Method为key的数组(注意不是映射map

截屏2023-05-12 23.20.53.png

  1. Gin中间件的部署方式是使用gin.Use()函数
// HandlerFunc defines the handler used by gin middleware as return value.
// 中间件类型的定义(不是中间件的定义,中间件和路由是同一种类型,想不到吧)
type HandlerFunc func(*Context)

// Use attaches a global middleware to the router. ie. the middleware attached though Use() will be
// included in the handlers chain for every single request. Even 404, 405, static files...
// For example, this is the right place for a logger or error management middleware.
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
   // 核心就是调用了RouterGroup的Use方法
   engine.RouterGroup.Use(middleware...)
   engine.rebuild404Handlers()
   engine.rebuild405Handlers()
   return engine
}
  1. Gin启动

    1. 初始化实例,路由设置,实例运行(engine.Run())
  2. Gin参数解析和绑定

    1. Context简析方法:

      1. handler信息
      2. 调用链控制
      3. 错误管理
      4. 上下文资源控制
      5. 获取参数
      6. 写响应

    bind相关操作:

    Bind和ShouldBind的区别在于,Bind如果解析错误将在Header中写入400,ShouldBind错误不会写入400

    Bind的Json底层使用了go的json

截屏2023-05-12 23.24.15.png

⚠️:几个开源库地址和简单作用

net/http -> fasthttp

github.com/valyala/fas…

作用:

  • 复用 goroutine,减轻 runtime 调度压力;
  • 对象复用,大量使用 sync.Pool 减轻 GC 压力。

除了复用,还有其他的一些优化手段,例如尽量避免 string 与 []byte 的转换开销等。

encoding/json -> jsoniter

github.com/json-iterat…

作用:

JSON 解析器,与标准库encoding/json兼容

方法:Marshal() 、 Unmarshal()

golang/protobuf -> gogo/protobuf

github.com/gogo/protob…

  • golang/protobuf更快地序列化与反序列化;
  • 更规范的 Go 结构;
  • 兼容golang/protobuf
  • 可选地生成额外的帮助代码,减少代码输入;
  • 可以生成测试代码和 benchmark 代码;
  • 其他序列化格式;

html/template -> valyala/quicktemplate

github.com/valyala/qui…

quicktemplate会先将编写的模板代码转换为 Go 语言代码,再进行编译渲染。因此,它比标准库html/template快 20 倍以上。quicktemplate的语法与 Go 语法非常类似,几乎没有学习成本。几乎所有的 bug 都能在模板编译时被捕获,因此在实际项目中,很少会有受模板相关的bug影响。模板中可以嵌入任意 Go 代码。

  • 总结:

  • GoWeb开发套装

    • Gin-提供了灵活的路由和中间件机制
    • Gorm-简化了与数据库的交互操作
    • viper-可以管理应用程序的配置
    • validator-验证数据的有效性
    • zap-记录应用程序的日志
    • go-redis-简化了对Redis的操作
    • grpc-实现了高性能的RPC通信