вводить
- ginкаркас, основанный наhttprouterРеализовать самый важный модуль маршрутизации и использовать структуру данных, подобную словарю, для хранения сопоставления между методами маршрутизации и обработки. Это также является причиной высокой производительности платформы. Заинтересованные студенты могут обращаться к ним самостоятельно.
- В этой статье представленыонлайн ментальная картаСо статьями, чтобы увидеть больше с меньшими усилиями
-
Engine
Объект-контейнер, основа всего фреймворка -
Engine.trees
Отвечает за хранение сопоставления методов маршрутизации и обработки с использованием структуры, похожей на дерево словаря. -
Engine.RouterGroup
, где обработчики хранят все промежуточное ПО -
Context
Объект контекста, отвечающий за обработку请求和回应
,один из нихhandlers
Он хранит промежуточное ПО и методы обработки при обработке запросов.
Инициализировать контейнер
позвонив
gin.New()
метод создания экземпляраEngine
. Хотя есть много параметров, нам нужно только обратить внимание наRouterGroup
,trees
а такжеengine.pool.New
Просто
-
engine.pool.New
ответственный за созданиеContext
объект, используяsync.Pool
Уменьшить потребление ресурсов, вызванное частым созданием контекста,
func New() *Engine {
debugPrintWARNINGNew()
engine := &Engine{
//实例化RouterGroup,其中Handlers为中间件数组
RouterGroup: RouterGroup{
Handlers: nil,
basePath: "/",
root: true,
},
FuncMap: template.FuncMap{},
RedirectTrailingSlash: true,
RedirectFixedPath: false,
HandleMethodNotAllowed: false,
ForwardedByClientIP: true,
AppEngine: defaultAppEngine,
UseRawPath: false,
RemoveExtraSlash: false,
UnescapePathValues: true,
MaxMultipartMemory: defaultMultipartMemory,
//trees 是最重要的点!!!!负责存储路由和handle方法的映射,采用类似字典树的结构
trees: make(methodTrees, 0, 9),
delims: render.Delims{Left: "{{", Right: "}}"},
secureJsonPrefix: "while(1);",
}
engine.RouterGroup.engine = engine
//这里采用 sync/pool 实现context池,减少频繁context实例化带来的资源消耗
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
return engine
}
зарегистрировать промежуточное ПО
Высокая производительность джина в основном зависит от
trees
, содержимое каждого узла можно представить какkey->value
словарное дерево,key
это маршрут иvalue
это[]HandlerFunc
, в котором хранитсяМетоды промежуточного программного обеспечения и контроллера обработки выполняются последовательно, вот очень важно, чтобы проверить!
Зарегистрировать глобальное промежуточное ПО
gin.use()
перечислитьRouterGroup.Use()
прошлоеRouterGroup.Handlers
записать запись
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...)
engine.rebuild404Handlers() //注册404处理方法
engine.rebuild405Handlers() //注册405处理方法
return engine
}
// 其中`Handlers`字段就是一个数组,用来存储中间件
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
Зарегистрировать ПО промежуточного слоя группы маршрутизации
- пройти через
Group()
метод возвращает вновь сгенерированныйRouterGroup
Указатель, используемый для разделения каждой группы маршрутизации для загрузки различного промежуточного программного обеспечения.- Обратите внимание здесь
Handlers: group.combineHandlers(handlers)
, эта строка кода скопирует копию глобального ПО промежуточного слоя во вновь сгенерированныйRouterGroup.Handlers
, при следующей регистрации маршрута его можно вместе записать в узел дерева
group := g.Group("/test_group")
group.Use(middleware.Test())
{
//这里会最终路由和中间件以及handle方法一起写入树节点中
group.GET("/test",handler.TestTool)
}
//返回一个RouterGroup指针
func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
return &RouterGroup{
//复制一份全局中间件
Handlers: group.combineHandlers(handlers),
basePath: group.calculateAbsolutePath(relativePath),
engine: group.engine,
}
}
Регистрация маршрутов и промежуточного ПО
Независимо от метода запроса, в конечном итоге он будет вызван
RouterGroup.handle
, этот метод имеет две основные функции
Обработать формат маршрута и записать маршрут в маршрут, начинающийся с символа «/».
Скопируйте RouterGroup.Handlers, добавьте метод handle, соответствующий этому маршруту, сформируйте список и поместите его в узел дерева
последний звонок
trees.addRoute
добавить узел
g.GET("/test_tool", middleware.Test(),handler.TestTool)
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
//根目录和路由结合起来,将路由拼成 '/' 字符开头的路由
absolutePath := group.calculateAbsolutePath(relativePath)
//复制一份RouterGroup.Handlers,加上相应这次路由的handle方法,组成一个list放入树节点中
handlers = group.combineHandlers(handlers)
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
//调用 `trees`增加节点
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
assert1(path[0] == '/', "path must begin with '/'")
assert1(method != "", "HTTP method can not be empty")
assert1(len(handlers) > 0, "there must be at least one handler")
debugPrintRoute(method, path, handlers)
root := engine.trees.get(method)
if root == nil {
root = new(node)
root.fullPath = "/"
engine.trees = append(engine.trees, methodTree{method: method, root: root})
}
root.addRoute(path, handlers)
}
запускать
позвонив
net/http
запустить службу, так какengine
ДостигнутоServeHTTP
метод, нужно только напрямую пройтиengine
Объект может быть инициализирован и запущен
g.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
}
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
//来自 net/http 定义的接口,只要实现了这个接口就可以作为处理请求的函数
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
//实现了ServeHTTP方法
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)
}
обработать запрос
- Просто обратите внимание здесь
handleHTTPRequest(c *Context)
метод в порядке- Найдите соответствующий узел дерева с помощью метода запроса и маршрута и получите сохраненный
[]HandlerFunc
список, позвонивc.Next()
обработать запрос- Постоянно рекурсивно перемещая нижний индекс, окончательно завершите обработку и верните результат
func (engine *Engine) handleHTTPRequest(c *Context) {
...
//
t := engine.trees
for i, tl := 0, len(t); i < tl; i++ {
...
// Find route in tree
value := root.getValue(rPath, c.Params, unescape)
if value.handlers != nil {
c.handlers = value.handlers
c.Params = value.params
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)
c.index++
}
}
впечатление
- Исходный код фреймворка gin относительно прост и понятен, что как раз и является его преимуществом. Сам язык golang относительно зрелый. Фреймворк - это просто каркас для вашего проекта. Вы можете настроить свой собственный в соответствии с вашими потребностями.
gin
Фреймворк, включая ведение журнала, кэширование, очереди и т. д. - Ядром является дерево хранения маршрутизации, хорошо изучающее алгоритм, а структура данных является ключом
Справочная документация
джин китайская документация
https://gin-gonic.com/zh-cn/docs/introduction/
адрес проекта Джин
https://github.com/gin-gonic/gin
httprouter
https://github.com/julienschmidt/httprouter